OAuth 2.0 и JWT авторизация на C++: на практике
Современные веб-приложения и микросервисы редко обходятся без систем авторизации. Два самых популярных стандарта — OAuth 2.0 для делегирования доступа и JWT (JSON Web Token) для компактной передачи данных — стали фактическим стандартом индустрии. В этой статье мы разберем, как реализовать OAuth 2.0 клиент и работу с JWT на C++. Вы получите готовые примеры кода и поймете, как интегрировать эти механизмы в ваш проект.
Что такое OAuth 2.0 и JWT: краткий обзор
OAuth 2.0 — это протокол авторизации, который позволяет приложениям получать ограниченный доступ к ресурсам пользователя без передачи его логина и пароля. Основные участники: Resource Owner (пользователь), Client (наше приложение), Authorization Server (сервер, выдающий токены) и Resource Server (сервер, предоставляющий данные).
JWT — это формат токена, состоящий из трех частей: Header (алгоритм подписи), Payload (данные, например, ID пользователя или роль) и Signature (подпись, защищающая от подделки). JWT часто используется как Bearer-токен в OAuth 2.0.
На C++ мы будем использовать библиотеки cpprestsdk (для HTTP-запросов) и jwt-cpp (для работы с JWT). Установите их через vcpkg:
vcpkg install cpprestsdk
vcpkg install jwt-cpp
Реализация OAuth 2.0 клиента на C++
Рассмотрим типичный сценарий: наше C++-приложение должно авторизоваться через Google OAuth 2.0. Для этого нам нужно: зарегистрировать приложение в Google Cloud Console, получить Client ID и Client Secret, а затем выполнить Authorization Code Flow.
Шаг 1: Формирование URL для авторизации
Первый шаг — перенаправить пользователя на страницу авторизации Google. Сформируем URL с необходимыми параметрами:
#include <string>
#include <cpprest/http_client.h>
#include <cpprest/uri_builder.h>
using namespace web;
using namespace web::http;
using namespace web::http::client;
std::string BuildAuthorizationUrl(const std::string& client_id,
const std::string& redirect_uri,
const std::string& state) {
uri_builder builder(U("https://accounts.google.com/o/oauth2/v2/auth"));
builder.append_query(U("client_id"), utility::conversions::to_string_t(client_id));
builder.append_query(U("redirect_uri"), utility::conversions::to_string_t(redirect_uri));
builder.append_query(U("response_type"), U("code"));
builder.append_query(U("scope"), U("openid%20email%20profile"));
builder.append_query(U("state"), utility::conversions::to_string_t(state));
builder.append_query(U("access_type"), U("offline"));
builder.append_query(U("prompt"), U("consent"));
return utility::conversions::to_utf8string(builder.to_uri().to_string());
}
Шаг 2: Обмен кода на токен
После того как пользователь подтвердит доступ, Google перенаправит его на наш redirect_uri с параметром code. Этот код нужно обменять на access_token и refresh_token:
pplx::task<web::json::value> ExchangeCodeForToken(
const std::string& client_id,
const std::string& client_secret,
const std::string& redirect_uri,
const std::string& code) {
http_client client(U("https://oauth2.googleapis.com/token"));
web::json::value body;
body[U("code")] = web::json::value::string(utility::conversions::to_string_t(code));
body[U("client_id")] = web::json::value::string(utility::conversions::to_string_t(client_id));
body[U("client_secret")] = web::json::value::string(utility::conversions::to_string_t(client_secret));
body[U("redirect_uri")] = web::json::value::string(utility::conversions::to_string_t(redirect_uri));
body[U("grant_type")] = web::json::value::string(U("authorization_code"));
http_request request(methods::POST);
request.headers().set_content_type(U("application/json"));
request.set_body(body);
return client.request(request).then([](http_response response) {
return response.extract_json();
});
}
// Использование:
auto token_response = ExchangeCodeForToken(client_id, client_secret, redirect_uri, code).get();
std::string access_token = utility::conversions::to_utf8string(
token_response[U("access_token")].as_string());
Работа с JWT токенами на C++
Теперь, когда у нас есть access_token (который может быть JWT), научимся его декодировать и верифицировать. Для этого используем библиотеку jwt-cpp.
Верификация JWT
Допустим, наш сервер выдаёт JWT с подписью HMAC-SHA256. Вот как проверить токен и извлечь данные:
#include <jwt-cpp/jwt.h>
#include <iostream>
struct JwtPayload {
std::string user_id;
std::string email;
std::chrono::system_clock::time_point exp;
};
std::optional<JwtPayload> VerifyJwt(const std::string& token,
const std::string& secret) {
try {
// Декодируем без проверки, чтобы получить заголовок
auto decoded = jwt::decode(token);
// Создаём верификатор с алгоритмом HS256
auto verifier = jwt::verify()
.allow_algorithm(jwt::algorithm::hs256{secret})
.with_issuer("auth.myapp.com");
verifier.verify(decoded);
// Извлекаем данные из payload
JwtPayload payload;
payload.user_id = decoded.get_payload_claim("sub").as_string();
payload.email = decoded.get_payload_claim("email").as_string();
payload.exp = decoded.get_expires_at();
return payload;
} catch (const std::exception& e) {
std::cerr << "JWT verification failed: " << e.what() << std::endl;
return std::nullopt;
}
}
Генерация JWT на сервере
Если ваше C++-приложение выступает в роли сервера, ему нужно уметь создавать JWT:
std::string CreateJwt(const std::string& user_id,
const std::string& email,
const std::string& secret) {
auto token = jwt::create