C++ REST SDK OAuth2

authentication-libraryC++OAuth2RESTMicrosoftHTTP-Clientsecurity

Authentication Library

C++ REST SDK OAuth2

Overview

C++ REST SDK OAuth2 is the OAuth 2.0 authentication functionality included in the Microsoft C++ REST SDK (cpprestsdk). As of 2025, this SDK has transitioned to maintenance mode, and while Microsoft does not recommend its use in new projects, critical bug fixes and security updates continue for existing systems. As one of the few available options for implementing OAuth 2.0 authentication in C++ applications, it provides standard OAuth 2.0 features including authorization code flow, implicit flow, and client authentication. Available through the web::http::oauth2::experimental namespace, it handles OAuth 2.0 configuration, token management, and error handling via oauth2_config, oauth2_token, and oauth2_exception classes.

Details

C++ REST SDK OAuth2 is a library for implementing OAuth 2.0 authentication in C++ environments. Key characteristics include:

  • OAuth 2.0 Standard Compliance: Implementation of authorization code flow and implicit flow in accordance with RFC 6749
  • Experimental API: Provided through web::http::oauth2::experimental namespace (requires attention to API stability)
  • Configuration Management: OAuth 2.0 configuration management through oauth2_config class
  • Token Management: Processing of access tokens and refresh tokens through oauth2_token class
  • Cross-Platform: Support for operation on Windows, macOS, and Linux
  • Microsoft Graph Integration: Design optimized for integration with Microsoft Graph API
  • HTTP Client Integration: Integration with cpprestsdk HTTP client functionality

Pros and Cons

Pros

  • OAuth 2.0 implementation in C++ native environment without dependency on other language libraries
  • High compatibility and support with Microsoft products and services
  • Consistent implementation across multiple OS environments through cross-platform support
  • Simplified Web API integration through unified HTTP operations and OAuth authentication
  • Easy integration into existing cpprestsdk projects
  • Optimized for integration with Microsoft ecosystem including Microsoft Graph, Azure, and Office 365

Cons

  • New feature development stopped due to maintenance mode, only security updates continue
  • Risk of future breaking changes due to experimental API
  • Microsoft does not recommend use in new projects
  • No support for latest authentication specifications like OAuth 2.1 and OpenID Connect
  • Limited documentation and community support
  • Complexity of C++ language learning costs and memory management
  • Limited alternative libraries and migration options

Reference Pages

Code Examples

Basic OAuth2 Configuration and Client Implementation

#include <cpprest/http_client.h>
#include <cpprest/oauth2.h>
#include <iostream>
#include <string>

using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::http::oauth2::experimental;

class OAuth2Client {
private:
    oauth2_config m_config;
    oauth2_token m_token;
    http_client m_http_client;
    
public:
    OAuth2Client(const std::string& client_id, 
                 const std::string& client_secret,
                 const std::string& auth_endpoint,
                 const std::string& token_endpoint,
                 const std::string& redirect_uri)
        : m_http_client(U("https://api.example.com")) {
        
        // Initialize OAuth2 configuration
        m_config.set_client_key(utility::conversions::to_string_t(client_id));
        m_config.set_client_secret(utility::conversions::to_string_t(client_secret));
        m_config.set_auth_endpoint(utility::conversions::to_string_t(auth_endpoint));
        m_config.set_token_endpoint(utility::conversions::to_string_t(token_endpoint));
        m_config.set_redirect_uri(utility::conversions::to_string_t(redirect_uri));
        
        // Configure scopes
        std::vector<utility::string_t> scopes = {
            U("read"),
            U("write"),
            U("profile")
        };
        m_config.set_scope(scopes);
    }
    
    // Generate authorization URL
    std::string build_authorization_uri() {
        auto auth_uri = m_config.build_authorization_uri(true); // Enable PKCE
        return utility::conversions::to_utf8string(auth_uri);
    }
    
    // Obtain access token from authorization code
    pplx::task<bool> request_token(const std::string& authorization_code) {
        utility::string_t code = utility::conversions::to_string_t(authorization_code);
        
        return m_config.token_from_code(code)
        .then([this](oauth2_token token) {
            m_token = token;
            std::wcout << L"Access token obtained: " 
                      << token.access_token().substr(0, 20) << L"..." << std::endl;
            return true;
        })
        .then([](pplx::task<bool> t) {
            try {
                return t.get();
            }
            catch (const oauth2_exception& ex) {
                std::wcout << L"OAuth2 error: " << ex.what() << std::endl;
                return false;
            }
            catch (const std::exception& ex) {
                std::wcout << L"Error: " << utility::conversions::to_string_t(ex.what()) << std::endl;
                return false;
            }
        });
    }
    
    // Update access token using refresh token
    pplx::task<bool> refresh_token() {
        if (m_token.refresh_token().empty()) {
            std::wcout << L"No refresh token available" << std::endl;
            return pplx::task_from_result(false);
        }
        
        return m_config.token_from_refresh(m_token)
        .then([this](oauth2_token new_token) {
            m_token = new_token;
            std::wcout << L"Token refreshed successfully" << std::endl;
            return true;
        })
        .then([](pplx::task<bool> t) {
            try {
                return t.get();
            }
            catch (const oauth2_exception& ex) {
                std::wcout << L"Token refresh error: " << ex.what() << std::endl;
                return false;
            }
        });
    }
    
    // Send authenticated HTTP request
    pplx::task<http_response> make_authenticated_request(const std::string& path, 
                                                        method request_method = methods::GET) {
        http_request request(request_method);
        request.set_request_uri(utility::conversions::to_string_t(path));
        
        // Add access token to Authorization header
        utility::string_t auth_header = U("Bearer ") + m_token.access_token();
        request.headers().add(header_names::authorization, auth_header);
        request.headers().add(header_names::content_type, U("application/json"));
        
        return m_http_client.request(request)
        .then([this](http_response response) {
            if (response.status_code() == status_codes::Unauthorized) {
                std::wcout << L"Token expired, attempting refresh..." << std::endl;
                return refresh_token()
                .then([this, request](bool success) {
                    if (success) {
                        // Retry after token refresh
                        http_request retry_request = request;
                        utility::string_t new_auth_header = U("Bearer ") + m_token.access_token();
                        retry_request.headers().remove(header_names::authorization);
                        retry_request.headers().add(header_names::authorization, new_auth_header);
                        return m_http_client.request(retry_request);
                    } else {
                        throw oauth2_exception("Failed to refresh token");
                    }
                });
            }
            return pplx::task_from_result(response);
        });
    }
    
    // Check token validity
    bool is_token_valid() const {
        if (m_token.access_token().empty()) {
            return false;
        }
        
        // Check token expiration (simplified version)
        auto now = utility::datetime::utc_now();
        return m_token.expires_in() > 0;
    }
    
    // Display token information
    void print_token_info() const {
        std::wcout << L"Token Type: " << m_token.token_type() << std::endl;
        std::wcout << L"Access Token: " << m_token.access_token().substr(0, 20) << L"..." << std::endl;
        std::wcout << L"Expires In: " << m_token.expires_in() << L" seconds" << std::endl;
        std::wcout << L"Scope: " << m_token.scope() << std::endl;
        
        if (!m_token.refresh_token().empty()) {
            std::wcout << L"Refresh Token: " << m_token.refresh_token().substr(0, 20) << L"..." << std::endl;
        }
    }
};

Microsoft Graph API Integration Implementation

#include <cpprest/http_client.h>
#include <cpprest/oauth2.h>
#include <cpprest/json.h>
#include <iostream>

using namespace web;
using namespace web::json;
using namespace web::http;
using namespace web::http::client;
using namespace web::http::oauth2::experimental;

class MicrosoftGraphClient {
private:
    oauth2_config m_config;
    oauth2_token m_token;
    http_client m_graph_client;
    
public:
    MicrosoftGraphClient(const std::string& client_id, 
                        const std::string& client_secret,
                        const std::string& tenant_id = "common")
        : m_graph_client(U("https://graph.microsoft.com/v1.0")) {
        
        // Configure Microsoft Azure AD v2.0 endpoints
        utility::string_t tenant = utility::conversions::to_string_t(tenant_id);
        utility::string_t auth_endpoint = U("https://login.microsoftonline.com/") + tenant + U("/oauth2/v2.0/authorize");
        utility::string_t token_endpoint = U("https://login.microsoftonline.com/") + tenant + U("/oauth2/v2.0/token");
        
        m_config.set_client_key(utility::conversions::to_string_t(client_id));
        m_config.set_client_secret(utility::conversions::to_string_t(client_secret));
        m_config.set_auth_endpoint(auth_endpoint);
        m_config.set_token_endpoint(token_endpoint);
        m_config.set_redirect_uri(U("http://localhost:8080/auth/callback"));
        
        // Configure Microsoft Graph scopes
        std::vector<utility::string_t> scopes = {
            U("https://graph.microsoft.com/User.Read"),
            U("https://graph.microsoft.com/Mail.Read"),
            U("https://graph.microsoft.com/Files.Read")
        };
        m_config.set_scope(scopes);
    }
    
    // Get user profile
    pplx::task<json::value> get_user_profile() {
        return make_graph_request("/me")
        .then([](http_response response) {
            return response.extract_json();
        })
        .then([](json::value profile) {
            std::wcout << L"User Profile Retrieved:" << std::endl;
            std::wcout << L"Name: " << profile[U("displayName")].as_string() << std::endl;
            std::wcout << L"Email: " << profile[U("mail")].as_string() << std::endl;
            std::wcout << L"ID: " << profile[U("id")].as_string() << std::endl;
            return profile;
        });
    }
    
    // Get user messages
    pplx::task<json::value> get_user_messages(int limit = 10) {
        utility::string_t path = U("/me/messages?$top=") + utility::conversions::to_string_t(std::to_string(limit));
        
        return make_graph_request(utility::conversions::to_utf8string(path))
        .then([](http_response response) {
            return response.extract_json();
        })
        .then([](json::value messages) {
            std::wcout << L"Messages Retrieved:" << std::endl;
            auto message_array = messages[U("value")].as_array();
            
            for (size_t i = 0; i < message_array.size(); ++i) {
                auto message = message_array[i];
                std::wcout << L"Subject: " << message[U("subject")].as_string() << std::endl;
                std::wcout << L"From: " << message[U("from")][U("emailAddress")][U("name")].as_string() << std::endl;
                std::wcout << L"---" << std::endl;
            }
            
            return messages;
        });
    }
    
    // Get OneDrive files
    pplx::task<json::value> get_drive_items() {
        return make_graph_request("/me/drive/root/children")
        .then([](http_response response) {
            return response.extract_json();
        })
        .then([](json::value files) {
            std::wcout << L"Drive Files:" << std::endl;
            auto file_array = files[U("value")].as_array();
            
            for (size_t i = 0; i < file_array.size(); ++i) {
                auto file = file_array[i];
                std::wcout << L"Name: " << file[U("name")].as_string();
                
                if (file.has_field(U("size"))) {
                    std::wcout << L" (" << file[U("size")].as_number().to_uint64() << L" bytes)";
                }
                std::wcout << std::endl;
            }
            
            return files;
        });
    }
    
    // Authenticated Graph API request
    pplx::task<http_response> make_graph_request(const std::string& path, 
                                                method request_method = methods::GET) {
        if (!is_token_valid()) {
            throw oauth2_exception("Invalid or expired token");
        }
        
        http_request request(request_method);
        request.set_request_uri(utility::conversions::to_string_t(path));
        
        // Configure Microsoft Graph headers
        utility::string_t auth_header = U("Bearer ") + m_token.access_token();
        request.headers().add(header_names::authorization, auth_header);
        request.headers().add(header_names::content_type, U("application/json"));
        request.headers().add(U("Accept"), U("application/json"));
        
        return m_graph_client.request(request)
        .then([](http_response response) {
            if (response.status_code() != status_codes::OK) {
                std::wcout << L"Graph API request failed: " << response.status_code() << std::endl;
                return response.extract_json().then([response](json::value error_json) {
                    std::wcout << L"Error details: " << error_json.serialize() << std::endl;
                    throw http_exception(response.status_code());
                    return response;
                });
            }
            return pplx::task_from_result(response);
        });
    }
    
    // Inherit or delegate methods from OAuth2Client class
    bool is_token_valid() const {
        return !m_token.access_token().empty() && m_token.expires_in() > 0;
    }
    
    void set_token(const oauth2_token& token) {
        m_token = token;
    }
    
    oauth2_config& get_config() {
        return m_config;
    }
};

Complete Authentication Flow and Error Handling

#include <cpprest/http_listener.h>
#include <cpprest/uri_builder.h>
#include <thread>
#include <future>

using namespace web::http::experimental::listener;

class OAuth2FlowManager {
private:
    std::unique_ptr<http_listener> m_listener;
    std::promise<std::string> m_auth_code_promise;
    std::future<std::string> m_auth_code_future;
    
public:
    OAuth2FlowManager() : m_auth_code_future(m_auth_code_promise.get_future()) {}
    
    // Start local HTTP server and wait for callback
    void start_callback_server(int port = 8080) {
        utility::string_t address = U("http://localhost:") + utility::conversions::to_string_t(std::to_string(port));
        m_listener = std::make_unique<http_listener>(address);
        
        m_listener->support(methods::GET, [this](http_request request) {
            auto query_params = uri::split_query(request.request_uri().query());
            auto code_it = query_params.find(U("code"));
            auto error_it = query_params.find(U("error"));
            
            if (error_it != query_params.end()) {
                std::string error_msg = utility::conversions::to_utf8string(error_it->second);
                std::wcout << L"OAuth error: " << error_it->second << std::endl;
                m_auth_code_promise.set_exception(std::make_exception_ptr(
                    oauth2_exception(error_msg.c_str())));
            } else if (code_it != query_params.end()) {
                std::string auth_code = utility::conversions::to_utf8string(code_it->second);
                std::wcout << L"Authorization code received: " << code_it->second.substr(0, 10) << L"..." << std::endl;
                m_auth_code_promise.set_value(auth_code);
            } else {
                m_auth_code_promise.set_exception(std::make_exception_ptr(
                    oauth2_exception("No authorization code received")));
            }
            
            // Return success page
            utility::string_t response_body = U(
                "<html><body><h2>Authorization completed!</h2>"
                "<p>You can close this window.</p></body></html>"
            );
            request.reply(status_codes::OK, response_body, U("text/html"));
        });
        
        m_listener->open().wait();
        std::wcout << L"Callback server started on " << address << std::endl;
    }
    
    // Execute complete authentication flow
    pplx::task<oauth2_token> complete_auth_flow(OAuth2Client& client) {
        return pplx::create_task([this, &client]() {
            // Step 1: Start local server
            start_callback_server();
            
            // Step 2: Generate and display authorization URL
            std::string auth_url = client.build_authorization_uri();
            std::wcout << L"Please visit the following URL to authorize the application:" << std::endl;
            std::wcout << utility::conversions::to_string_t(auth_url) << std::endl;
            std::wcout << L"Waiting for authorization..." << std::endl;
            
            // Step 3: Wait for user authorization completion
            std::string auth_code;
            try {
                // Wait for authorization code with timeout (5 minutes)
                auto timeout = std::chrono::minutes(5);
                if (m_auth_code_future.wait_for(timeout) == std::future_status::timeout) {
                    throw oauth2_exception("Authorization timeout");
                }
                auth_code = m_auth_code_future.get();
            } catch (const std::exception& ex) {
                m_listener->close().wait();
                throw;
            }
            
            // Step 4: Exchange authorization code for access token
            return client.request_token(auth_code).then([this](bool success) {
                m_listener->close().wait();
                if (!success) {
                    throw oauth2_exception("Failed to exchange authorization code for token");
                }
                return oauth2_token(); // Return actual token (implementation required)
            });
        });
    }
    
    ~OAuth2FlowManager() {
        if (m_listener) {
            m_listener->close().wait();
        }
    }
};

// Usage example and error handling
int main() {
    try {
        // OAuth2 client configuration
        OAuth2Client client(
            "your-client-id",
            "your-client-secret", 
            "https://accounts.google.com/o/oauth2/v2/auth",
            "https://oauth2.googleapis.com/token",
            "http://localhost:8080/auth/callback"
        );
        
        OAuth2FlowManager flow_manager;
        
        // Execute complete authentication flow
        auto token_task = flow_manager.complete_auth_flow(client);
        
        token_task.then([&client](oauth2_token token) {
            std::wcout << L"Authentication successful!" << std::endl;
            client.print_token_info();
            
            // Test authenticated API request
            return client.make_authenticated_request("/api/user/profile");
        })
        .then([](http_response response) {
            return response.extract_json();
        })
        .then([](json::value profile_data) {
            std::wcout << L"Profile data: " << profile_data.serialize() << std::endl;
        })
        .then([](pplx::task<void> t) {
            try {
                t.get();
            } catch (const oauth2_exception& ex) {
                std::wcout << L"OAuth2 error: " << ex.what() << std::endl;
            } catch (const http_exception& ex) {
                std::wcout << L"HTTP error: " << ex.what() << L" (" << ex.error_code() << L")" << std::endl;
            } catch (const std::exception& ex) {
                std::wcout << L"General error: " << utility::conversions::to_string_t(ex.what()) << std::endl;
            }
        }).wait();
        
    } catch (const std::exception& ex) {
        std::wcout << L"Application error: " << utility::conversions::to_string_t(ex.what()) << std::endl;
        return 1;
    }
    
    return 0;
}

Token Persistence and Auto-Refresh

#include <fstream>
#include <cpprest/json.h>

class TokenStorage {
public:
    // Save token to file
    static bool save_token(const oauth2_token& token, const std::string& filename = "oauth_token.json") {
        try {
            json::value token_json = json::value::object();
            token_json[U("access_token")] = json::value::string(token.access_token());
            token_json[U("refresh_token")] = json::value::string(token.refresh_token());
            token_json[U("token_type")] = json::value::string(token.token_type());
            token_json[U("expires_in")] = json::value::number(token.expires_in());
            token_json[U("scope")] = json::value::string(token.scope());
            
            std::ofstream file(filename);
            file << utility::conversions::to_utf8string(token_json.serialize());
            file.close();
            
            std::wcout << L"Token saved to " << utility::conversions::to_string_t(filename) << std::endl;
            return true;
        } catch (const std::exception& ex) {
            std::wcout << L"Error saving token: " << utility::conversions::to_string_t(ex.what()) << std::endl;
            return false;
        }
    }
    
    // Load token from file
    static std::unique_ptr<oauth2_token> load_token(const std::string& filename = "oauth_token.json") {
        try {
            std::ifstream file(filename);
            if (!file.is_open()) {
                std::wcout << L"Token file not found: " << utility::conversions::to_string_t(filename) << std::endl;
                return nullptr;
            }
            
            std::string json_string((std::istreambuf_iterator<char>(file)),
                                   std::istreambuf_iterator<char>());
            file.close();
            
            auto token_json = json::value::parse(utility::conversions::to_string_t(json_string));
            
            // Reconstruct oauth2_token (actual implementation depends on library version)
            auto token = std::make_unique<oauth2_token>();
            // Note: Actual setter methods for oauth2_token class are implementation-dependent
            
            std::wcout << L"Token loaded from " << utility::conversions::to_string_t(filename) << std::endl;
            return token;
        } catch (const std::exception& ex) {
            std::wcout << L"Error loading token: " << utility::conversions::to_string_t(ex.what()) << std::endl;
            return nullptr;
        }
    }
    
    // Check token expiration (time-based)
    static bool is_token_expired(const oauth2_token& token) {
        // Implementation note: oauth2_token class may not have absolute time information for expiration
        // In such cases, expires_in and issue time need to be managed separately
        return token.expires_in() <= 0;
    }
};

// Auto token management class
class AutoRefreshOAuth2Client : public OAuth2Client {
public:
    AutoRefreshOAuth2Client(const std::string& client_id, 
                           const std::string& client_secret,
                           const std::string& auth_endpoint,
                           const std::string& token_endpoint,
                           const std::string& redirect_uri)
        : OAuth2Client(client_id, client_secret, auth_endpoint, token_endpoint, redirect_uri) {
        
        // Attempt to restore saved token
        auto saved_token = TokenStorage::load_token();
        if (saved_token && !TokenStorage::is_token_expired(*saved_token)) {
            // Use token if valid
            set_token(*saved_token);
            std::wcout << L"Using saved token" << std::endl;
        }
    }
    
    // Authenticated request with auto-refresh
    pplx::task<http_response> make_request_with_auto_refresh(const std::string& path, 
                                                           method request_method = methods::GET) {
        return make_authenticated_request(path, request_method)
        .then([this](pplx::task<http_response> response_task) {
            try {
                auto response = response_task.get();
                
                // Save token on successful request
                if (response.status_code() == status_codes::OK) {
                    TokenStorage::save_token(get_current_token());
                }
                
                return response;
            } catch (const oauth2_exception& ex) {
                std::wcout << L"Request failed, clearing saved token" << std::endl;
                TokenStorage::save_token(oauth2_token()); // Overwrite with empty token
                throw;
            }
        });
    }
    
private:
    oauth2_token get_current_token() const {
        // Get current token (implementation depends on private member access)
        return oauth2_token(); // Placeholder
    }
    
    void set_token(const oauth2_token& token) {
        // Set token (implementation depends on private member access)
    }
};