OAuth C++ライブラリ

認証OAuth2C++Google Cloudliboauth2OpenID ConnectAPI

ライブラリ

OAuth C++ライブラリ

概要

C++アプリケーション向けのOAuth 2.0認証ライブラリで、Google Cloud C++ OAuth2ライブラリとliboauth2(OpenIDC)が主要な選択肢です。

詳細

C++でのOAuth 2.0実装には複数のライブラリオプションがあります。最も成熟したソリューションはGoogle Cloud C++ OAuth2ライブラリで、Googleサービスとの統合に特化しており、アプリケーションデフォルト認証情報(ADC)を活用した簡単な認証フローを提供します。このライブラリはoauth2::AccessTokenGeneratorインターフェースを中心に設計されており、継続的にメンテナンスされています。

もう一つの主要な選択肢はliboauth2(OpenIDC)で、汎用的なOAuth 2.0およびOpenID Connect実装のためのCライブラリです。サーバーとクライアントの両方を構築可能で、Webサーバープラグインの作成に適しています。mod_auth_openidcの基盤として使用されており、商用サポートも利用可能です。

その他のオプションとして、oauth2cpp(GitHub - bschramke/oauth2cpp)があり、RFC 6749に準拠した認可コードグラント実装を提供しますが、開発段階です。POCO C++ライブラリも基本的なOAuth認証情報クラスを含んでいます。Qt 5.8以降では、QtNetworkAuthenticationモジュールがOAuth 1.0と2.0をサポートしています。

これらのライブラリは、エンタープライズアプリケーション、クラウドサービス統合、APIアクセス管理など、様々な用途で使用されています。選択は、使用するプラットフォーム、統合するサービス、必要な機能によって決まります。

メリット・デメリット

メリット

  • 高性能: C++のネイティブ性能を活用した高速な認証処理
  • エンタープライズ対応: 大規模システムでの実績と安定性
  • Google統合: Google Cloud C++ライブラリによるシームレスなGoogle API統合
  • 標準準拠: RFC 6749およびOpenID Connect仕様に完全準拠
  • 商用サポート: liboauth2では商用サポートが利用可能
  • **WebサーバーEcho": mod_auth_openidcとの統合によるWebサーバープラグイン対応

デメリット

  • 学習コスト: C++とOAuth 2.0仕様の深い理解が必要
  • ドキュメント不足: 一部ライブラリでは包括的なドキュメントが不足
  • 複雑性: 低レベルAPI操作による実装の複雑さ
  • プラットフォーム依存: OS固有の実装が必要な場合がある
  • メモリ管理: C++特有のメモリ管理の責任
  • 開発速度: 他言語と比較して開発速度が遅い場合がある

主要リンク

書き方の例

Google Cloud C++ OAuth2基本実装

#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() {
        // アプリケーションデフォルト認証情報を使用
        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();
            
            // HTTPリクエストヘッダーにトークンを設定
            std::cout << "Making request to: " << url << std::endl;
            std::cout << "Authorization: Bearer " << token.substr(0, 20) << "..." << std::endl;
            
            // 実際のHTTPリクエスト実装はここに追加
            
        } catch (const std::exception& e) {
            std::cerr << "Request failed: " << e.what() << std::endl;
            throw;
        }
    }
};

int main() {
    try {
        OAuth2Client client;
        
        // 認証されたAPIリクエストの実行
        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;
    }
}

カスタムOAuth2認証情報プロバイダー

#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;
        
        // POSTデータを準備
        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);
        
        // 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;
    }
};

// 使用例
int main() {
    try {
        CustomOAuth2Provider provider(
            "your-client-id",
            "your-client-secret",  
            "https://oauth.example.com/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;
        
        // リフレッシュトークンを使用してアクセストークンを更新
        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実装

#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設定
        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(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");
        
        // アクセストークンをAuthorizationヘッダーに設定
        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;
        
        // APIリクエストの例
        makeApiRequest("https://api.example.com/user");
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    QtOAuth2Client client;
    
    // 認証開始
    client.authenticate();
    
    return app.exec();
}

#include "main.moc"

PKCE対応OAuth2実装

#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);
        
        // Base64URLエンコーディングのための文字置換
        for (char& c : result) {
            if (c == '+') c = '-';
            else if (c == '/') c = '_';
        }
        
        // パディング除去
        result.erase(result.find_last_not_of('=') + 1);
        
        return result;
    }
    
    std::string generateCodeChallenge(const std::string& verifier) {
        // SHA256ハッシュ計算
        std::vector<unsigned char> hash(SHA256_DIGEST_LENGTH);
        SHA256(reinterpret_cast<const unsigned char*>(verifier.c_str()), 
               verifier.length(), hash.data());
        
        // Base64URLエンコード
        return base64UrlEncode(hash);
    }

public:
    PKCEOAuth2Client() {
        // コードベリファイアーを生成(43-128文字のランダム文字列)
        code_verifier_ = generateRandomString(128);
        
        // コードチャレンジを生成(SHA256ハッシュ + Base64URLエンコード)
        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_);
        
        // HTTPリクエスト実行(実装は省略、実際にはlibcurlなどを使用)
        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. 認証URLを生成
        std::string auth_url = getAuthorizationUrl(client_id, redirect_uri, scope, state);
        
        std::cout << "1. 認証URLを生成しました:" << std::endl;
        std::cout << auth_url << std::endl << std::endl;
        
        std::cout << "2. PKCEパラメータ:" << 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. ユーザーを認証URLにリダイレクトして認可コードを取得してください" << std::endl;
        std::cout << "4. 取得した認可コードでexchangeCodeForToken()を呼び出してください" << 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) {
        // 実際の実装ではlibcurlまたは同等のHTTPライブラリを使用
        // ここではダミーレスポンスを返す
        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;
        // 実際の実装ではJSON解析ライブラリを使用
        // ここでは簡単な例として固定値を設定
        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;
        
        // PKCEフローのデモンストレーション
        pkce_client.demonstrateFlow();
        
        return 0;
        
    } catch (const std::exception& e) {
        std::cerr << "PKCE OAuth2 Error: " << e.what() << std::endl;
        return 1;
    }
}

エラーハンドリングとロギング

#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();
        
        // コンソールにも出力(エラーと警告のみ)
        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;
                
                // トークンの有効期限を設定
                auto now = std::chrono::system_clock::now();
                token_expiry_ = now + std::chrono::seconds(token_response.expires_in - 60); // 60秒のマージン
                
                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());
                
                // リトライ可能なエラーかチェック
                if (!isRetryableError(e.getErrorCode())) {
                    logger_.log(OAuth2Logger::LogLevel::ERROR, "Non-retryable error, aborting");
                    throw;
                }
                
                // レート制限の場合は特別な待機時間
                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;
                    }
                }
                
                // 通常のリトライ待機
                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);
            
            // HTTPリクエスト実行(実装は省略)
            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) {
        // エラーメッセージからRetry-After値を抽出
        // 実装は省略、実際にはHTTPヘッダーから取得
        return 0;
    }
    
    TokenResponse requestAccessToken() {
        // Client Credentialsフローでアクセストークンを要求
        // 実際の実装では HTTP POST リクエストを実行
        
        // デモ用の実装
        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) {
        // 実際のHTTPリクエスト実行
        // libcurl等を使用してAuthorizationヘッダーにBearerトークンを設定
        
        // デモ用の実装
        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
        );
        
        // セキュアなAPIコール
        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;
    }
}