OAuth C++ Libraries

AuthenticationOAuth2C++Google Cloudliboauth2OpenID ConnectAPI

Authentication Library

OAuth C++ Libraries

Overview

OAuth 2.0 authentication libraries for C++ applications, with Google Cloud C++ OAuth2 Library and liboauth2 (OpenIDC) being the primary choices.

Details

C++ OAuth 2.0 implementation offers multiple library options. The most mature solution is the Google Cloud C++ OAuth2 Library, which specializes in Google services integration and provides simple authentication flows leveraging Application Default Credentials (ADC). This library is designed around the oauth2::AccessTokenGenerator interface and is continuously maintained.

Another major option is liboauth2 (OpenIDC), a generic C library for OAuth 2.0 and OpenID Connect implementation. It can build both servers and clients and is suitable for creating web server plugins. It serves as the foundation for mod_auth_openidc and offers commercial support.

Other options include oauth2cpp (GitHub - bschramke/oauth2cpp), which provides RFC 6749-compliant authorization code grant implementation but is still in development. POCO C++ Libraries also include basic OAuth credential classes. Qt 5.8 and later support OAuth 1.0 and 2.0 through the QtNetworkAuthentication module.

These libraries are used in various applications including enterprise applications, cloud service integration, and API access management. The choice depends on the platform used, services to integrate, and required features.

Pros and Cons

Pros

  • High Performance: Fast authentication processing leveraging C++ native performance
  • Enterprise Ready: Proven stability and track record in large-scale systems
  • Google Integration: Seamless Google API integration through Google Cloud C++ Library
  • Standards Compliance: Full compliance with RFC 6749 and OpenID Connect specifications
  • Commercial Support: Commercial support available for liboauth2
  • Web Server Integration: Web server plugin support through mod_auth_openidc integration

Cons

  • Learning Curve: Requires deep understanding of C++ and OAuth 2.0 specifications
  • Limited Documentation: Some libraries lack comprehensive documentation
  • Complexity: Implementation complexity due to low-level API operations
  • Platform Dependency: Platform-specific implementation may be required
  • Memory Management: Responsibility for C++-specific memory management
  • Development Speed: May be slower development compared to other languages

Key Links

Code Examples

Google Cloud C++ OAuth2 Basic Implementation

#include "google/cloud/oauth2/access_token_generator.h"
#include "google/cloud/credentials.h"
#include <iostream>
#include <memory>

class OAuth2Client {
private:
    std::unique_ptr<google::cloud::oauth2::AccessTokenGenerator> generator_;

public:
    OAuth2Client() {
        // Use Application Default Credentials
        auto credentials = google::cloud::MakeGoogleDefaultCredentials();
        if (!credentials) {
            throw std::runtime_error("Failed to create default credentials");
        }
        
        generator_ = google::cloud::oauth2::MakeAccessTokenGenerator(*credentials);
    }
    
    std::string getAccessToken() {
        auto token = generator_->GetToken();
        if (!token) {
            throw std::runtime_error("Failed to get access token: " + token.status().message());
        }
        
        return token->token;
    }
    
    bool isTokenExpired(const std::string& token) {
        auto current_token = generator_->GetToken();
        if (!current_token) return true;
        
        auto expiry = current_token->expiration;
        auto now = std::chrono::system_clock::now();
        
        return expiry <= now;
    }
    
    void makeAuthenticatedRequest(const std::string& url) {
        try {
            std::string token = getAccessToken();
            
            // Set token in HTTP request header
            std::cout << "Making request to: " << url << std::endl;
            std::cout << "Authorization: Bearer " << token.substr(0, 20) << "..." << std::endl;
            
            // Actual HTTP request implementation goes here
            
        } catch (const std::exception& e) {
            std::cerr << "Request failed: " << e.what() << std::endl;
            throw;
        }
    }
};

int main() {
    try {
        OAuth2Client client;
        
        // Execute authenticated API request
        client.makeAuthenticatedRequest("https://api.example.com/data");
        
        return 0;
        
    } catch (const std::exception& e) {
        std::cerr << "OAuth2 Client Error: " << e.what() << std::endl;
        return 1;
    }
}

Custom OAuth2 Credentials Provider

#include "google/cloud/oauth2/access_token_generator.h"
#include "google/cloud/credentials.h"
#include <json/json.h>
#include <curl/curl.h>
#include <memory>

class CustomOAuth2Provider {
private:
    std::string client_id_;
    std::string client_secret_;
    std::string token_url_;
    std::string refresh_token_;
    
    struct HTTPResponse {
        std::string data;
        long response_code;
    };
    
    static size_t WriteCallback(void* contents, size_t size, size_t nmemb, HTTPResponse* response) {
        size_t total_size = size * nmemb;
        response->data.append(static_cast<char*>(contents), total_size);
        return total_size;
    }

public:
    CustomOAuth2Provider(const std::string& client_id, 
                        const std::string& client_secret,
                        const std::string& token_url)
        : client_id_(client_id), client_secret_(client_secret), token_url_(token_url) {}
    
    struct TokenResponse {
        std::string access_token;
        std::string refresh_token;
        int expires_in;
        std::string token_type;
    };
    
    TokenResponse exchangeAuthorizationCode(const std::string& auth_code, 
                                           const std::string& redirect_uri) {
        CURL* curl = curl_easy_init();
        if (!curl) {
            throw std::runtime_error("Failed to initialize CURL");
        }
        
        HTTPResponse response;
        
        // Prepare POST data
        std::string post_data = "grant_type=authorization_code"
                               "&client_id=" + urlEncode(client_id_) +
                               "&client_secret=" + urlEncode(client_secret_) +
                               "&code=" + urlEncode(auth_code) +
                               "&redirect_uri=" + urlEncode(redirect_uri);
        
        // Configure CURL
        curl_easy_setopt(curl, CURLOPT_URL, token_url_.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        
        struct curl_slist* headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        
        CURLcode res = curl_easy_perform(curl);
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.response_code);
        
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
        
        if (res != CURLE_OK) {
            throw std::runtime_error("CURL request failed: " + std::string(curl_easy_strerror(res)));
        }
        
        if (response.response_code != 200) {
            throw std::runtime_error("Token exchange failed. HTTP " + std::to_string(response.response_code) + 
                                   ": " + response.data);
        }
        
        return parseTokenResponse(response.data);
    }
    
    TokenResponse refreshAccessToken(const std::string& refresh_token) {
        CURL* curl = curl_easy_init();
        if (!curl) {
            throw std::runtime_error("Failed to initialize CURL");
        }
        
        HTTPResponse response;
        
        std::string post_data = "grant_type=refresh_token"
                               "&client_id=" + urlEncode(client_id_) +
                               "&client_secret=" + urlEncode(client_secret_) +
                               "&refresh_token=" + urlEncode(refresh_token);
        
        curl_easy_setopt(curl, CURLOPT_URL, token_url_.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        
        struct curl_slist* headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        
        CURLcode res = curl_easy_perform(curl);
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.response_code);
        
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
        
        if (res != CURLE_OK) {
            throw std::runtime_error("CURL request failed: " + std::string(curl_easy_strerror(res)));
        }
        
        if (response.response_code != 200) {
            throw std::runtime_error("Token refresh failed. HTTP " + std::to_string(response.response_code) + 
                                   ": " + response.data);
        }
        
        return parseTokenResponse(response.data);
    }

private:
    std::string urlEncode(const std::string& value) {
        CURL* curl = curl_easy_init();
        if (!curl) return value;
        
        char* encoded = curl_easy_escape(curl, value.c_str(), value.length());
        if (!encoded) {
            curl_easy_cleanup(curl);
            return value;
        }
        
        std::string result(encoded);
        curl_free(encoded);
        curl_easy_cleanup(curl);
        
        return result;
    }
    
    TokenResponse parseTokenResponse(const std::string& json_response) {
        Json::Value root;
        Json::Reader reader;
        
        if (!reader.parse(json_response, root)) {
            throw std::runtime_error("Failed to parse JSON response");
        }
        
        TokenResponse token;
        token.access_token = root.get("access_token", "").asString();
        token.refresh_token = root.get("refresh_token", "").asString();
        token.expires_in = root.get("expires_in", 3600).asInt();
        token.token_type = root.get("token_type", "Bearer").asString();
        
        if (token.access_token.empty()) {
            throw std::runtime_error("No access token in response");
        }
        
        return token;
    }
};

// Usage example
int main() {
    try {
        CustomOAuth2Provider provider(
            "your-client-id",
            "your-client-secret",  
            "https://oauth.example.com/token"
        );
        
        // Exchange authorization code for access token
        std::string auth_code = "received-authorization-code";
        std::string redirect_uri = "https://your-app.com/callback";
        
        auto token_response = provider.exchangeAuthorizationCode(auth_code, redirect_uri);
        
        std::cout << "Access Token: " << token_response.access_token.substr(0, 20) << "..." << std::endl;
        std::cout << "Expires in: " << token_response.expires_in << " seconds" << std::endl;
        
        // Refresh access token using refresh token
        if (!token_response.refresh_token.empty()) {
            auto refreshed = provider.refreshAccessToken(token_response.refresh_token);
            std::cout << "Refreshed Token: " << refreshed.access_token.substr(0, 20) << "..." << std::endl;
        }
        
        return 0;
        
    } catch (const std::exception& e) {
        std::cerr << "OAuth2 Error: " << e.what() << std::endl;
        return 1;
    }
}

Qt NetworkAuth OAuth2 Implementation

#include <QApplication>
#include <QOAuth2AuthorizationCodeFlow>
#include <QDesktopServices>
#include <QUrlQuery>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <iostream>

class QtOAuth2Client : public QObject {
    Q_OBJECT

private:
    QOAuth2AuthorizationCodeFlow* oauth2_;
    QString access_token_;

public:
    QtOAuth2Client(QObject* parent = nullptr) : QObject(parent) {
        oauth2_ = new QOAuth2AuthorizationCodeFlow(this);
        
        // OAuth2 configuration
        oauth2_->setScope("read write");
        oauth2_->setClientIdentifier("your-client-id");
        oauth2_->setClientIdentifierSharedKey("your-client-secret");
        
        oauth2_->setAuthorizationUrl(QUrl("https://oauth.example.com/authorize"));
        oauth2_->setAccessTokenUrl(QUrl("https://oauth.example.com/token"));
        
        // Connect signals
        connect(oauth2_, &QOAuth2AuthorizationCodeFlow::statusChanged,
                this, &QtOAuth2Client::onStatusChanged);
        connect(oauth2_, &QOAuth2AuthorizationCodeFlow::granted,
                this, &QtOAuth2Client::onGranted);
        connect(oauth2_, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
                &QDesktopServices::openUrl);
    }
    
public slots:
    void authenticate() {
        oauth2_->grant();
    }
    
    void makeApiRequest(const QString& url) {
        if (access_token_.isEmpty()) {
            std::cerr << "No access token available" << std::endl;
            return;
        }
        
        QNetworkRequest request;
        request.setUrl(QUrl(url));
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        
        // Set access token in Authorization header
        QString auth_header = "Bearer " + access_token_;
        request.setRawHeader("Authorization", auth_header.toUtf8());
        
        QNetworkReply* reply = oauth2_->networkAccessManager()->get(request);
        
        connect(reply, &QNetworkReply::finished, [reply, this]() {
            if (reply->error() == QNetworkReply::NoError) {
                QByteArray data = reply->readAll();
                QJsonDocument doc = QJsonDocument::fromJson(data);
                
                std::cout << "API Response: " << doc.toJson().toStdString() << std::endl;
            } else {
                std::cerr << "API Request failed: " << reply->errorString().toStdString() << std::endl;
            }
            
            reply->deleteLater();
        });
    }

private slots:
    void onStatusChanged(QAbstractOAuth::Status status) {
        switch (status) {
            case QAbstractOAuth::Status::NotAuthenticated:
                std::cout << "Not authenticated" << std::endl;
                break;
            case QAbstractOAuth::Status::TemporaryCredentialsReceived:
                std::cout << "Temporary credentials received" << std::endl;
                break;
            case QAbstractOAuth::Status::Granted:
                std::cout << "Access granted" << std::endl;
                break;
            case QAbstractOAuth::Status::RefreshingToken:
                std::cout << "Refreshing token" << std::endl;
                break;
        }
    }
    
    void onGranted() {
        access_token_ = oauth2_->token();
        std::cout << "Authentication successful!" << std::endl;
        std::cout << "Access Token: " << access_token_.left(20).toStdString() << "..." << std::endl;
        
        // Example API request
        makeApiRequest("https://api.example.com/user");
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    QtOAuth2Client client;
    
    // Start authentication
    client.authenticate();
    
    return app.exec();
}

#include "main.moc"

PKCE-Enabled OAuth2 Implementation

#include <iostream>
#include <string>
#include <random>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

class PKCEOAuth2Client {
private:
    std::string code_verifier_;
    std::string code_challenge_;
    
    std::string generateRandomString(size_t length) {
        const std::string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dist(0, charset.size() - 1);
        
        std::string result;
        result.reserve(length);
        
        for (size_t i = 0; i < length; ++i) {
            result += charset[dist(gen)];
        }
        
        return result;
    }
    
    std::string base64UrlEncode(const std::vector<unsigned char>& data) {
        BIO* bio = BIO_new(BIO_s_mem());
        BIO* b64 = BIO_new(BIO_f_base64());
        BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
        bio = BIO_push(b64, bio);
        
        BIO_write(bio, data.data(), data.size());
        BIO_flush(bio);
        
        BUF_MEM* bufferPtr;
        BIO_get_mem_ptr(bio, &bufferPtr);
        
        std::string result(bufferPtr->data, bufferPtr->length);
        BIO_free_all(bio);
        
        // Character replacement for Base64URL encoding
        for (char& c : result) {
            if (c == '+') c = '-';
            else if (c == '/') c = '_';
        }
        
        // Remove padding
        result.erase(result.find_last_not_of('=') + 1);
        
        return result;
    }
    
    std::string generateCodeChallenge(const std::string& verifier) {
        // Calculate SHA256 hash
        std::vector<unsigned char> hash(SHA256_DIGEST_LENGTH);
        SHA256(reinterpret_cast<const unsigned char*>(verifier.c_str()), 
               verifier.length(), hash.data());
        
        // Base64URL encode
        return base64UrlEncode(hash);
    }

public:
    PKCEOAuth2Client() {
        // Generate code verifier (43-128 character random string)
        code_verifier_ = generateRandomString(128);
        
        // Generate code challenge (SHA256 hash + Base64URL encode)
        code_challenge_ = generateCodeChallenge(code_verifier_);
    }
    
    std::string getAuthorizationUrl(const std::string& client_id,
                                   const std::string& redirect_uri,
                                   const std::string& scope,
                                   const std::string& state) {
        
        std::string auth_url = "https://oauth.example.com/authorize"
                               "?response_type=code"
                               "&client_id=" + urlEncode(client_id) +
                               "&redirect_uri=" + urlEncode(redirect_uri) +
                               "&scope=" + urlEncode(scope) +
                               "&state=" + urlEncode(state) +
                               "&code_challenge=" + urlEncode(code_challenge_) +
                               "&code_challenge_method=S256";
        
        return auth_url;
    }
    
    struct TokenResponse {
        std::string access_token;
        std::string refresh_token;
        int expires_in;
        std::string token_type;
        std::string scope;
    };
    
    TokenResponse exchangeCodeForToken(const std::string& client_id,
                                     const std::string& authorization_code,
                                     const std::string& redirect_uri) {
        
        std::string post_data = "grant_type=authorization_code"
                               "&client_id=" + urlEncode(client_id) +
                               "&code=" + urlEncode(authorization_code) +
                               "&redirect_uri=" + urlEncode(redirect_uri) +
                               "&code_verifier=" + urlEncode(code_verifier_);
        
        // Execute HTTP request (implementation omitted, use libcurl in actual implementation)
        std::string response_json = makeHttpPost("https://oauth.example.com/token", post_data);
        
        return parseTokenResponse(response_json);
    }
    
    void demonstrateFlow() {
        std::string client_id = "your-public-client-id";
        std::string redirect_uri = "https://your-app.com/callback";
        std::string scope = "read write";
        std::string state = generateRandomString(32);
        
        // 1. Generate authorization URL
        std::string auth_url = getAuthorizationUrl(client_id, redirect_uri, scope, state);
        
        std::cout << "1. Generated authorization URL:" << std::endl;
        std::cout << auth_url << std::endl << std::endl;
        
        std::cout << "2. PKCE parameters:" << std::endl;
        std::cout << "Code Verifier: " << code_verifier_ << std::endl;
        std::cout << "Code Challenge: " << code_challenge_ << std::endl;
        std::cout << "Code Challenge Method: S256" << std::endl << std::endl;
        
        std::cout << "3. Redirect user to authorization URL to obtain authorization code" << std::endl;
        std::cout << "4. Call exchangeCodeForToken() with the obtained authorization code" << std::endl;
    }

private:
    std::string urlEncode(const std::string& value) {
        std::ostringstream escaped;
        escaped.fill('0');
        escaped << std::hex;
        
        for (char c : value) {
            if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
                escaped << c;
            } else {
                escaped << std::uppercase;
                escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
                escaped << std::nouppercase;
            }
        }
        
        return escaped.str();
    }
    
    std::string makeHttpPost(const std::string& url, const std::string& data) {
        // Use libcurl or equivalent HTTP library in actual implementation
        // Return dummy response here
        return R"({
            "access_token": "example_access_token_12345",
            "token_type": "Bearer",
            "expires_in": 3600,
            "refresh_token": "example_refresh_token_67890",
            "scope": "read write"
        })";
    }
    
    TokenResponse parseTokenResponse(const std::string& json) {
        TokenResponse response;
        // Use JSON parsing library in actual implementation
        // Set fixed values as simple example here
        response.access_token = "example_access_token_12345";
        response.token_type = "Bearer";
        response.expires_in = 3600;
        response.refresh_token = "example_refresh_token_67890";
        response.scope = "read write";
        
        return response;
    }
};

int main() {
    try {
        PKCEOAuth2Client pkce_client;
        
        std::cout << "=== PKCE OAuth2 Client Demo ===" << std::endl << std::endl;
        
        // Demonstrate PKCE flow
        pkce_client.demonstrateFlow();
        
        return 0;
        
    } catch (const std::exception& e) {
        std::cerr << "PKCE OAuth2 Error: " << e.what() << std::endl;
        return 1;
    }
}

Error Handling and Logging

#include <iostream>
#include <fstream>
#include <chrono>
#include <iomanip>
#include <sstream>

class OAuth2Logger {
private:
    std::ofstream log_file_;
    
    std::string getCurrentTimestamp() {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);
        
        std::stringstream ss;
        ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
        return ss.str();
    }

public:
    OAuth2Logger(const std::string& log_file_path = "oauth2_client.log") 
        : log_file_(log_file_path, std::ios::app) {}
    
    enum class LogLevel {
        DEBUG,
        INFO, 
        WARNING,
        ERROR
    };
    
    void log(LogLevel level, const std::string& message) {
        std::string level_str;
        switch (level) {
            case LogLevel::DEBUG:   level_str = "DEBUG"; break;
            case LogLevel::INFO:    level_str = "INFO"; break;
            case LogLevel::WARNING: level_str = "WARNING"; break;
            case LogLevel::ERROR:   level_str = "ERROR"; break;
        }
        
        std::string log_entry = "[" + getCurrentTimestamp() + "] " + 
                               level_str + ": " + message;
        
        log_file_ << log_entry << std::endl;
        log_file_.flush();
        
        // Also output to console (errors and warnings only)
        if (level == LogLevel::ERROR || level == LogLevel::WARNING) {
            std::cerr << log_entry << std::endl;
        }
    }
};

class RobustOAuth2Client {
private:
    OAuth2Logger logger_;
    std::string client_id_;
    std::string client_secret_;
    std::string token_url_;
    std::string current_token_;
    std::chrono::system_clock::time_point token_expiry_;
    int max_retries_;
    int retry_delay_ms_;

public:
    RobustOAuth2Client(const std::string& client_id,
                      const std::string& client_secret,
                      const std::string& token_url,
                      int max_retries = 3,
                      int retry_delay_ms = 1000)
        : client_id_(client_id), client_secret_(client_secret), 
          token_url_(token_url), max_retries_(max_retries), 
          retry_delay_ms_(retry_delay_ms) {
        
        logger_.log(OAuth2Logger::LogLevel::INFO, "OAuth2 Client initialized");
    }
    
    enum class OAuth2Error {
        NETWORK_ERROR,
        INVALID_CLIENT,
        INVALID_GRANT,
        INVALID_REQUEST,
        UNAUTHORIZED_CLIENT,
        UNSUPPORTED_GRANT_TYPE,
        INVALID_SCOPE,
        RATE_LIMITED,
        SERVER_ERROR,
        UNKNOWN_ERROR
    };
    
    class OAuth2Exception : public std::exception {
    private:
        OAuth2Error error_code_;
        std::string message_;
        int http_status_;
        
    public:
        OAuth2Exception(OAuth2Error error_code, const std::string& message, int http_status = 0)
            : error_code_(error_code), message_(message), http_status_(http_status) {}
        
        const char* what() const noexcept override {
            return message_.c_str();
        }
        
        OAuth2Error getErrorCode() const { return error_code_; }
        int getHttpStatus() const { return http_status_; }
    };
    
    std::string getAccessTokenWithRetry() {
        if (isTokenValid()) {
            return current_token_;
        }
        
        OAuth2Exception last_exception(OAuth2Error::UNKNOWN_ERROR, "Unknown error");
        
        for (int attempt = 1; attempt <= max_retries_; ++attempt) {
            try {
                logger_.log(OAuth2Logger::LogLevel::INFO, 
                          "Attempting token request (attempt " + std::to_string(attempt) + "/" + 
                          std::to_string(max_retries_) + ")");
                
                auto token_response = requestAccessToken();
                current_token_ = token_response.access_token;
                
                // Set token expiration time
                auto now = std::chrono::system_clock::now();
                token_expiry_ = now + std::chrono::seconds(token_response.expires_in - 60); // 60 second margin
                
                logger_.log(OAuth2Logger::LogLevel::INFO, "Access token obtained successfully");
                return current_token_;
                
            } catch (const OAuth2Exception& e) {
                last_exception = e;
                
                logger_.log(OAuth2Logger::LogLevel::ERROR,
                          "Token request failed (attempt " + std::to_string(attempt) + "): " + e.what());
                
                // Check if error is retryable
                if (!isRetryableError(e.getErrorCode())) {
                    logger_.log(OAuth2Logger::LogLevel::ERROR, "Non-retryable error, aborting");
                    throw;
                }
                
                // Special wait time for rate limiting
                if (e.getErrorCode() == OAuth2Error::RATE_LIMITED) {
                    int wait_time = extractRetryAfter(e.what());
                    if (wait_time > 0) {
                        logger_.log(OAuth2Logger::LogLevel::INFO,
                                  "Rate limited, waiting " + std::to_string(wait_time) + " seconds");
                        std::this_thread::sleep_for(std::chrono::seconds(wait_time));
                        continue;
                    }
                }
                
                // Normal retry wait
                if (attempt < max_retries_) {
                    int wait_time = retry_delay_ms_ * attempt;
                    logger_.log(OAuth2Logger::LogLevel::INFO,
                              "Waiting " + std::to_string(wait_time) + "ms before retry");
                    std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
                }
            }
        }
        
        logger_.log(OAuth2Logger::LogLevel::ERROR, 
                  "Failed to obtain access token after " + std::to_string(max_retries_) + " attempts");
        throw last_exception;
    }
    
    bool makeSecureApiCall(const std::string& url, const std::string& method = "GET") {
        try {
            std::string token = getAccessTokenWithRetry();
            
            logger_.log(OAuth2Logger::LogLevel::INFO, 
                      "Making " + method + " request to: " + url);
            
            // Execute HTTP request (implementation omitted)
            bool success = executeHttpRequest(url, method, token);
            
            if (success) {
                logger_.log(OAuth2Logger::LogLevel::INFO, "API call successful");
            } else {
                logger_.log(OAuth2Logger::LogLevel::ERROR, "API call failed");
            }
            
            return success;
            
        } catch (const OAuth2Exception& e) {
            logger_.log(OAuth2Logger::LogLevel::ERROR, 
                      "API call failed due to OAuth2 error: " + std::string(e.what()));
            return false;
        } catch (const std::exception& e) {
            logger_.log(OAuth2Logger::LogLevel::ERROR, 
                      "API call failed due to unexpected error: " + std::string(e.what()));
            return false;
        }
    }

private:
    struct TokenResponse {
        std::string access_token;
        std::string token_type;
        int expires_in;
    };
    
    bool isTokenValid() {
        if (current_token_.empty()) return false;
        
        auto now = std::chrono::system_clock::now();
        return now < token_expiry_;
    }
    
    bool isRetryableError(OAuth2Error error) {
        switch (error) {
            case OAuth2Error::NETWORK_ERROR:
            case OAuth2Error::RATE_LIMITED:
            case OAuth2Error::SERVER_ERROR:
                return true;
            default:
                return false;
        }
    }
    
    int extractRetryAfter(const std::string& error_message) {
        // Extract Retry-After value from error message
        // Implementation omitted, actually extract from HTTP headers
        return 0;
    }
    
    TokenResponse requestAccessToken() {
        // Request access token with Client Credentials flow
        // Execute HTTP POST request in actual implementation
        
        // Demo implementation
        TokenResponse response;
        response.access_token = "demo_access_token_" + std::to_string(std::time(nullptr));
        response.token_type = "Bearer";
        response.expires_in = 3600;
        
        return response;
    }
    
    bool executeHttpRequest(const std::string& url, const std::string& method, const std::string& token) {
        // Execute actual HTTP request
        // Use libcurl etc. to set Bearer token in Authorization header
        
        // Demo implementation
        return true;
    }
};

int main() {
    try {
        RobustOAuth2Client client(
            "your-client-id",
            "your-client-secret", 
            "https://oauth.example.com/token",
            3,   // max_retries
            2000 // retry_delay_ms
        );
        
        // Secure API call
        bool success = client.makeSecureApiCall("https://api.example.com/secure-data");
        
        if (success) {
            std::cout << "API call completed successfully" << std::endl;
        } else {
            std::cout << "API call failed" << std::endl;
        }
        
        return 0;
        
    } catch (const std::exception& e) {
        std::cerr << "Application error: " << e.what() << std::endl;
        return 1;
    }
}