OAuth C++ライブラリ
ライブラリ
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 Reference
- Google APIs Client Library for C++
- liboauth2 GitHub Repository
- OAuth2cpp GitHub Repository
- Qt Network Authentication
- POCO C++ Libraries OAuth
- RFC 6749 - OAuth 2.0 Authorization Framework
書き方の例
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;
}
}