2#ifndef _KURLYK_HTTP_AUTH_OAUTH_PKCE_CLIENT_HPP_INCLUDED
3#define _KURLYK_HTTP_AUTH_OAUTH_PKCE_CLIENT_HPP_INCLUDED
18#if KURLYK_JSON_SUPPORT
19# include <nlohmann/json.hpp>
35 const std::string& raw_response,
37 std::string& out_error)>;
53 if (
m_config.authorization_endpoint.empty() ||
64 params.emplace(
"response_type",
"code");
65 params.emplace(
"client_id",
m_config.client_id);
66 params.emplace(
"redirect_uri",
m_config.redirect_uri);
68 params.emplace(
"scope",
m_config.scope);
70 params.emplace(
"state",
m_state);
81 params.emplace(
"audience",
m_config.audience);
84 params.emplace(
"prompt",
m_config.prompt);
87 params.emplace(
"access_type",
m_config.access_type);
106 if (
m_config.token_endpoint.empty()) {
108 out_result.
error_message =
"token_endpoint is not configured";
113 body_params.emplace(
"grant_type",
"authorization_code");
114 body_params.emplace(
"code", code);
115 body_params.emplace(
"redirect_uri",
m_config.redirect_uri);
116 body_params.emplace(
"client_id",
m_config.client_id);
117 if (!
m_config.client_secret.empty()) {
118 body_params.emplace(
"client_secret",
m_config.client_secret);
126 headers.emplace(
"Content-Type",
"application/x-www-form-urlencoded");
129 (void)request_result.first;
133 response = request_result.second.get();
134 }
catch (
const std::exception& e) {
136 out_result.
error_message = std::string(
"HTTP request failed: ") + e.what();
142 out_result.
error_message =
"HTTP request returned null response";
148 if (response->status_code < 200 || response->status_code >= 300) {
151 std::to_string(response->status_code);
165 if (refresh_token.empty()) {
171 if (
m_config.token_endpoint.empty()) {
173 out_result.
error_message =
"token_endpoint is not configured";
178 body_params.emplace(
"grant_type",
"refresh_token");
179 body_params.emplace(
"refresh_token", refresh_token);
180 body_params.emplace(
"client_id",
m_config.client_id);
181 if (!
m_config.client_secret.empty()) {
182 body_params.emplace(
"client_secret",
m_config.client_secret);
187 headers.emplace(
"Content-Type",
"application/x-www-form-urlencoded");
190 (void)request_result.first;
194 response = request_result.second.get();
195 }
catch (
const std::exception& e) {
197 out_result.
error_message = std::string(
"HTTP request failed: ") + e.what();
203 out_result.
error_message =
"HTTP request returned null response";
209 if (response->status_code < 200 || response->status_code >= 300) {
212 std::to_string(response->status_code);
238#if KURLYK_JSON_SUPPORT
240 nlohmann::json j = nlohmann::json::parse(raw_response);
242 if (j.contains(
"error")) {
245 j.value(
"error",
"unknown error"));
251 if (j.contains(
"access_token")) token.
access_token = j[
"access_token"].get<std::string>();
252 if (j.contains(
"refresh_token")) token.
refresh_token = j[
"refresh_token"].get<std::string>();
253 if (j.contains(
"token_type")) token.
token_type = j[
"token_type"].get<std::string>();
254 if (j.contains(
"scope")) token.
scope = j[
"scope"].get<std::string>();
256 if (j.contains(
"expires_in")) {
257 int64_t expires_in = j[
"expires_in"].get<int64_t>();
258 auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
259 std::chrono::system_clock::now().time_since_epoch()).count();
265 out_result.
error_message =
"access_token is missing in response";
269 out_result.
token = token;
272 }
catch (
const std::exception& e) {
274 out_result.
error_message = std::string(
"JSON parse error: ") + e.what();
279 std::string error_msg;
283 out_result.
error_message = error_msg.empty() ?
"custom token parser failed" : error_msg;
291 out_result.
error_message =
"JSON support disabled and no custom token parser set";
Defines authentication result types and error codes.
Defines the OAuthConfig structure for OAuth2 client configuration.
Provides PKCE (Proof Key for Code Exchange) utilities per RFC 7636.
std::string m_code_challenge
bool exchange_code(const std::string &code, AuthResult &out_result)
Exchanges an authorization code for an access token.
TokenParser m_token_parser
std::function< bool( const std::string &raw_response, OAuthToken &out_token, std::string &out_error)> TokenParser
Callback type for custom token parsing when JSON support is disabled.
std::string build_authorization_url()
Builds the full authorization URL with query parameters.
bool refresh_access_token(const std::string &refresh_token, AuthResult &out_result)
Refreshes an access token using a refresh token.
const std::string & state() const
Returns the state parameter used in the last authorization request.
bool validate_state(const std::string &returned_state) const
Validates the state parameter returned by the authorization server.
void set_token_parser(TokenParser parser)
Sets a custom token parser for non-JSON builds.
const std::string & code_verifier() const
Returns the code verifier used in the last PKCE exchange.
OAuthPkceClient(const OAuthConfig &config)
Constructs a client with the given OAuth configuration.
std::string m_code_verifier
bool parse_token_response(const std::string &raw_response, AuthResult &out_result)
Contains utility functions for handling HTTP requests, rate limiting, and request cancellation.
Provides utility functions for parsing HTTP headers and cookies.
std::string to_query_string(const QueryParams &query, const std::string &prefix=std::string()) noexcept
Converts a map of query parameters into a URL query string.
PkcePair make_pkce_pair()
Creates a PKCE pair with a freshly generated verifier.
std::string generate_code_verifier(std::size_t length=64)
Generates a cryptographically strong PKCE code verifier.
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
std::unique_ptr< HttpResponse > HttpResponsePtr
Owning pointer to an HTTP response.
utils::CaseInsensitiveMultimap Headers
Alias for HTTP headers, providing a case-insensitive unordered multimap.
@ HttpError
HTTP request failed (non-2xx status or transport error).
@ InvalidResponse
Server response could not be parsed.
@ InvalidConfig
Missing or invalid client configuration.
@ UnsupportedFlow
The requested OAuth flow is not supported.
utils::CaseInsensitiveMultimap QueryParams
Alias for query parameters in HTTP requests, stored case-insensitively.
uint64_t http_post(const std::string &url, const QueryParams &query, const Headers &headers, const std::string &content, HttpResponseCallback callback)
Sends an asynchronous HTTP POST request with a callback.
Provides functions for percent-encoding and decoding strings.
Encapsulates the outcome of an authentication operation.
std::string error_message
Human-readable error description (if any).
AuthError error
Error classification.
std::string raw_response
Raw server response body for diagnostics.
bool success
Whether the operation succeeded.
OAuthToken token
Token data on success (may be partially filled on failure).
Stores client configuration for an OAuth2 Authorization Code + PKCE flow.
Holds the result of an OAuth2 token exchange.
int64_t expires_at_ms
Absolute expiration time in milliseconds since epoch (0 = unknown).
std::string raw_response
Raw server response body for debugging.
std::string scope
Granted scope (may be empty).
std::string access_token
The access token string.
std::string token_type
Token type, typically "Bearer".
std::string refresh_token
The refresh token string (may be empty).
Stores PKCE verifier and challenge values.
std::string code_challenge_method
Challenge method, always "S256".
std::string code_verifier
Randomly generated code verifier.
std::string code_challenge
Derived S256 code challenge.