### GitHub OAuth2 Example Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt A complete example demonstrating how to authenticate with GitHub using OAuth2, including generating authorization URLs and exchanging codes for access tokens. ```APIDOC ## GitHub OAuth2 Example Complete example for GitHub authentication. ### Method ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope, TokenResponse, TokenUrl, }; fn main() -> Result<(), Box> { let github_client_id = ClientId::new("your_github_client_id".to_string()); let github_client_secret = ClientSecret::new("your_github_client_secret".to_string()); let client = BasicClient::new(github_client_id) .set_client_secret(github_client_secret) .set_auth_uri(AuthUrl::new("https://github.com/login/oauth/authorize".to_string())?) .set_token_uri(TokenUrl::new("https://github.com/login/oauth/access_token".to_string())?) .set_redirect_uri(RedirectUrl::new("http://localhost:8080".to_string())?); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build()?; // Generate authorization URL let (auth_url, csrf_state) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("public_repo".to_string())) .add_scope(Scope::new("user:email".to_string())) .url(); println!("Open this URL in your browser:\n{}\n", auth_url); // After user authorizes, exchange code for token // (In practice, you'd get this from your callback handler) let code = AuthorizationCode::new("code_from_github_callback".to_string()); let token = client.exchange_code(code).request(&http_client)?; println!("GitHub access token: {}", token.access_token().secret()); // GitHub returns comma-separated scopes if let Some(scopes) = token.scopes() { let parsed_scopes: Vec<_> = scopes .iter() .flat_map(|s| s.split(',')) .collect(); println!("Scopes: {:?}", parsed_scopes); } Ok(()) } ``` ``` -------------------------------- ### GitHub OAuth2 Authentication Example in Rust Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Provides a complete example of how to authenticate with GitHub using OAuth2 in a Rust application. It covers setting up the client, generating an authorization URL, and exchanging an authorization code for an access token. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope, TokenResponse, TokenUrl, }; fn main() -> Result<(), Box> { let github_client_id = ClientId::new("your_github_client_id".to_string()); let github_client_secret = ClientSecret::new("your_github_client_secret".to_string()); let client = BasicClient::new(github_client_id) .set_client_secret(github_client_secret) .set_auth_uri(AuthUrl::new("https://github.com/login/oauth/authorize".to_string())?) .set_token_uri(TokenUrl::new("https://github.com/login/oauth/access_token".to_string())?) .set_redirect_uri(RedirectUrl::new("http://localhost:8080".to_string())?); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build()?; // Generate authorization URL let (auth_url, csrf_state) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("public_repo".to_string())) .add_scope(Scope::new("user:email".to_string())) .url(); println!("Open this URL in your browser:\n{}\n", auth_url); // After user authorizes, exchange code for token // (In practice, you'd get this from your callback handler) let code = AuthorizationCode::new("code_from_github_callback".to_string()); let token = client.exchange_code(code).request(&http_client)?; println!("GitHub access token: {}", token.access_token().secret()); // GitHub returns comma-separated scopes if let Some(scopes) = token.scopes() { let parsed_scopes: Vec<_> = scopes .iter() .flat_map(|s| s.split(',')) .collect(); println!("Scopes: {:?}", parsed_scopes); } Ok(()) } ``` -------------------------------- ### Authorization Code Grant with PKCE (Synchronous) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Provides a step-by-step guide for implementing the Authorization Code Grant with PKCE using the synchronous `reqwest` client. ```APIDOC ## Authorization Code Grant with PKCE (Synchronous) ### Description This section details the implementation of the Authorization Code Grant with Proof Key for Code Exchange (PKCE). It's the recommended flow for most applications, especially public clients, ensuring enhanced security. ### Method Synchronous HTTP Request (using `reqwest`) ### Endpoint N/A (Client-side implementation of OAuth2 flow) ### Parameters N/A (Configuration and flow parameters are handled within the Rust code) ### Request Example ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthorizationCode, AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse, TokenUrl, }; // Create the client let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()) .set_redirect_uri(RedirectUrl::new("http://localhost:8080".to_string()).unwrap()); // Generate PKCE challenge and verifier let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); // Generate the authorization URL let (auth_url, csrf_token) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("read".to_string())) .add_scope(Scope::new("write".to_string())) .set_pkce_challenge(pkce_challenge) .url(); println!("Open this URL in your browser: {}", auth_url); // Create HTTP client (IMPORTANT: disable redirects to prevent SSRF) let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); // After user authorization, exchange the code for a token let authorization_code = AuthorizationCode::new("code_from_callback".to_string()); let token_result = client .exchange_code(authorization_code) .set_pkce_verifier(pkce_verifier) .request(&http_client) .unwrap(); // Access the token response println!("Access token: {}", token_result.access_token().secret()); println!("Token type: {:?}", token_result.token_type()); if let Some(expires_in) = token_result.expires_in() { println!("Expires in: {:?}", expires_in); } if let Some(refresh_token) = token_result.refresh_token() { println!("Refresh token: {}", refresh_token.secret()); } ``` ### Response #### Success Response (200 OK) - **access_token** (string) - The obtained access token. - **token_type** (string) - The type of the token (e.g., "Bearer"). - **expires_in** (integer, optional) - The lifetime in seconds of the access token. - **refresh_token** (string, optional) - The refresh token, which can be used to obtain a new access token. #### Response Example ```json { "access_token": "SlAV32hkT...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "..." } ``` ``` -------------------------------- ### Rust: Device Authorization Flow (RFC 8628) with OAuth2.rs Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Implements the Device Authorization Flow, suitable for browserless or input-constrained devices. It guides the user to authorize the application via a separate device and then polls for token issuance. Uses `oauth2-rs` and `reqwest`. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, ClientId, ClientSecret, DeviceAuthorizationUrl, Scope, StandardDeviceAuthorizationResponse, TokenResponse, TokenUrl, }; let device_auth_url = DeviceAuthorizationUrl::new("https://auth.example.com/device".to_string()).unwrap(); let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()) .set_device_authorization_url(device_auth_url); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); // Request device authorization let details: StandardDeviceAuthorizationResponse = client .exchange_device_code() .add_scope(Scope::new("read".to_string())) .request(&http_client) .unwrap(); // Display instructions to user println!( "Open this URL in your browser:\n{}\nand enter the code: {}", details.verification_uri().to_string(), details.user_code().secret().to_string() ); // Poll for token (automatically handles authorization_pending and slow_down) let token_result = client .exchange_device_access_token(&details) .request(&http_client, std::thread::sleep, None) .unwrap(); println!("Access token: {}", token_result.access_token().secret()); ``` -------------------------------- ### Configure ureq Client Redirection Policy Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md To prevent Server-Side Request Forgery (SSRF) vulnerabilities, HTTP clients should be configured not to follow redirects. This example demonstrates how to disable redirects when using the `ureq` crate. ```Rust let agent = ureq::AgentBuilder::new() .redirects(0) .build(); ``` -------------------------------- ### Configure reqwest Client Redirection Policy Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md To prevent Server-Side Request Forgery (SSRF) vulnerabilities, HTTP clients should be configured not to follow redirects. This example demonstrates how to disable redirects when using the `reqwest` crate. ```Rust let client = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) .build() .unwrap(); ``` -------------------------------- ### Creating a Basic OAuth2 Client Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates how to initialize and configure a `BasicClient` with essential OAuth2 parameters. ```APIDOC ## Creating an OAuth2 Client ### Description This section details the creation of a `BasicClient`, the primary entry point for OAuth2 operations in this library. It utilizes a builder pattern with typestates for configuration. ### Method Builder Pattern (Instantiation and Configuration) ### Endpoint N/A (Client-side configuration) ### Parameters N/A (Configuration is done via method calls on the builder) ### Request Example ```rust use oauth2::basic::BasicClient; use oauth2::{ AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl, }; // Create an OAuth2 client with required configuration let client = BasicClient::new(ClientId::new("my_client_id".to_string())) .set_client_secret(ClientSecret::new("my_client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://authorization-server.com/auth".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://authorization-server.com/token".to_string()).unwrap()) .set_redirect_uri(RedirectUrl::new("http://localhost:8080/callback".to_string()).unwrap()); // Client ID and auth type are accessible println!("Client ID: {}", client.client_id()); ``` ### Response N/A (This is client-side code) #### Success Response (N/A) N/A #### Response Example (N/A) N/A ``` -------------------------------- ### Initialize Basic OAuth2 Client Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates how to instantiate a BasicClient using the builder pattern. This sets up the necessary client credentials and endpoint URIs required for OAuth2 operations. ```rust use oauth2::basic::BasicClient; use oauth2::{ AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl, }; let client = BasicClient::new(ClientId::new("my_client_id".to_string())) .set_client_secret(ClientSecret::new("my_client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://authorization-server.com/auth".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://authorization-server.com/token".to_string()).unwrap()) .set_redirect_uri(RedirectUrl::new("http://localhost:8080/callback".to_string()).unwrap()); println!("Client ID: {}", client.client_id()); ``` -------------------------------- ### Authorization Code Grant with PKCE (Async) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Implements the Authorization Code Grant flow with Proof Key for Code Exchange (PKCE) using asynchronous HTTP requests. This is a secure flow suitable for public clients like SPAs and mobile apps. It requires setting up a BasicClient with necessary URIs and credentials, generating PKCE challenge/verifier, obtaining an authorization URL, and then exchanging the authorization code for an access token asynchronously. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthorizationCode, AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse, TokenUrl, }; async fn get_token() -> Result<(), Box> { let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string())?) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string())?) .set_redirect_uri(RedirectUrl::new("http://localhost:8080".to_string())?); let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let (auth_url, csrf_token) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("openid".to_string())) .set_pkce_challenge(pkce_challenge) .url(); // Async HTTP client let http_client = reqwest::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build()?; // Exchange code asynchronously let token_result = client .exchange_code(AuthorizationCode::new("authorization_code".to_string())) .set_pkce_verifier(pkce_verifier) .request_async(&http_client) .await?; println!("Access token: {}", token_result.access_token().secret()); Ok(()) } ``` -------------------------------- ### Authorization Code Grant with PKCE (Asynchronous) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates the asynchronous implementation of the Authorization Code Grant flow with Proof Key for Code Exchange (PKCE). This is the recommended flow for public clients and mobile applications. ```APIDOC ## Authorization Code Grant with PKCE (Asynchronous) ### Description The asynchronous version uses the same API but with `request_async` instead of `request`. ### Method POST (Implicitly via `request_async`) ### Endpoint (Not explicitly defined, handled by the `oauth2` crate) ### Parameters (Configuration parameters are set on the `BasicClient`) ### Request Example (Code example provided in Rust) ### Response #### Success Response (200) - **access_token** (string) - The access token issued by the authorization server. - **token_type** (string) - The type of the token (e.g., 'Bearer'). - **expires_in** (number) - The lifetime in seconds of the access token. - **refresh_token** (string, optional) - The refresh token, if issued. - **scope** (string, optional) - The scope of the access token, if issued. #### Response Example ```json { "access_token": "SlAV32hkYJ_1j8_aQ7aX", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "8X2fbsdkfjsdklfjsdklfjsdklf", "scope": "openid profile email" } ``` ``` -------------------------------- ### Implement Stateful HTTP Clients with Traits Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md Version 5.0 introduces `AsyncHttpClient` and `SyncHttpClient` traits for managing HTTP requests. These traits allow for stateful clients, enabling connection reuse and easier customization. Implementations are provided for `reqwest::Client`, `ureq::Agent`, `oauth2::CurlHttpClient`, and custom function types. It is crucial to configure clients to not follow redirects to prevent SSRF vulnerabilities. ```Rust trait AsyncHttpClient { fn request_async<'a>( &'a self, request: HttpRequest, ) -> F where Self: Send + Sync + 'a, E: std::error::Error + 'static, F: Future> + Send + 'a; } trait SyncHttpClient { fn request<'a>(&'a self, request: HttpRequest) -> Result where Self: Send + Sync + 'a, E: std::error::Error + 'static; } ``` ```Rust impl AsyncHttpClient for reqwest::Client { // ... implementation details ... } impl SyncHttpClient for reqwest::blocking::Client { // ... implementation details ... } ``` ```Rust impl AsyncHttpClient for Box { // ... implementation details ... } impl SyncHttpClient for Box { // ... implementation details ... } ``` -------------------------------- ### Execute Authorization Code Grant with PKCE Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Implements the secure Authorization Code flow with PKCE. It generates a challenge, constructs the authorization URL, and exchanges the authorization code for an access token using a synchronous HTTP client. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthorizationCode, AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()) .set_redirect_uri(RedirectUrl::new("http://localhost:8080".to_string()).unwrap()); let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); let (auth_url, csrf_token) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("read".to_string())) .add_scope(Scope::new("write".to_string())) .set_pkce_challenge(pkce_challenge) .url(); println!("Open this URL in your browser: {}", auth_url); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let authorization_code = AuthorizationCode::new("code_from_callback".to_string()); let token_result = client .exchange_code(authorization_code) .set_pkce_verifier(pkce_verifier) .request(&http_client) .unwrap(); println!("Access token: {}", token_result.access_token().secret()); ``` -------------------------------- ### Client Credentials Grant Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Illustrates the Client Credentials Grant flow, suitable for server-to-server authentication where the application acts on its own behalf. It requires setting up a BasicClient with token URI and then exchanging client credentials for an access token using `exchange_client_credentials()`. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, ClientId, ClientSecret, Scope, TokenResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let token_result = client .exchange_client_credentials() .add_scope(Scope::new("api:read".to_string())) .request(&http_client) .unwrap(); println!("Access token: {}", token_result.access_token().secret()); ``` -------------------------------- ### POST /device/code (Device Authorization) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Initiates the device authorization flow for input-constrained devices. ```APIDOC ## POST /device/code ### Description Initiates the device authorization flow for browserless or input-constrained devices. ### Method POST ### Endpoint /device/code ### Request Body - **client_id** (string) - Required - **scope** (string) - Optional ### Request Example { "client_id": "client_id", "scope": "read" } ### Response #### Success Response (200) - **device_code** (string) - Code used to poll for tokens - **user_code** (string) - Code to be entered by the user - **verification_uri** (string) - URL for the user to visit #### Response Example { "device_code": "dev_123", "user_code": "ABCD-1234", "verification_uri": "https://auth.example.com/device" } ``` -------------------------------- ### Implement Custom HTTP Client in Rust Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Shows how to implement the SyncHttpClient trait using a custom function. This allows developers to override default HTTP behavior when performing OAuth2 exchanges. ```rust use oauth2::{HttpRequest, HttpResponse, SyncHttpClient}; use std::error::Error; fn my_http_client(request: HttpRequest) -> Result> { let response = http::Response::builder() .status(200) .header("content-type", "application/json") .body(b"{\"access_token\":\"token\",\"token_type\":\"bearer\"}".to_vec()) .unwrap(); Ok(response) } let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let token = client .exchange_client_credentials() .request(&my_http_client) .unwrap(); ``` -------------------------------- ### Implicit Grant Flow Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates the Implicit Grant flow where tokens are returned directly from the authorization endpoint. This flow is less secure and generally not recommended. It involves generating an authorization URL with `use_implicit_flow()` and the access token is expected in the URL fragment after user authorization. ```rust use oauth2::basic::BasicClient; use oauth2::{AuthUrl, ClientId, CsrfToken, RedirectUrl, Scope}; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()); // Generate authorization URL for implicit flow let (auth_url, csrf_token) = client .authorize_url(CsrfToken::new_random) .use_implicit_flow() .url(); println!("Browse to: {}", auth_url); // The access token will be returned in the URL fragment after user authorization ``` -------------------------------- ### Handle OAuth2 Errors in Rust Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates how to handle different types of errors that can occur during OAuth2 operations using the oauth2-rs library. It covers server response errors, HTTP request failures, parsing issues, and other miscellaneous errors. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthorizationCode, AuthUrl, ClientId, ClientSecret, RequestTokenError, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .unwrap(); let result = client .exchange_code(AuthorizationCode::new("invalid_code".to_string())) .request(&http_client); match result { Ok(token) => { println!("Token received: {}", token.access_token().secret()); } Err(RequestTokenError::ServerResponse(err)) => { // OAuth2 error response from server println!("Server error: {:?}", err.error()); if let Some(desc) = err.error_description() { println!("Description: {}", desc); } } Err(RequestTokenError::Request(err)) => { // HTTP request failed println!("HTTP request error: {}", err); } Err(RequestTokenError::Parse(err, body)) => { // Failed to parse response println!("Parse error: {}, body: {:?}", err, body); } Err(RequestTokenError::Other(msg)) => { // Other error println!("Other error: {}", msg); } } ``` -------------------------------- ### Implicit Grant Flow Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Illustrates the Implicit Grant flow, where tokens are returned directly from the authorization endpoint. Use with caution due to security considerations. ```APIDOC ## Implicit Grant Flow ### Description The Implicit Grant returns tokens directly from the authorization endpoint. Use with caution as it's less secure than Authorization Code Grant. ### Method GET (Authorization URL generation) ### Endpoint (Authorization URL is generated, actual token exchange is via URL fragment) ### Parameters (Configuration parameters are set on the `BasicClient`) ### Request Example (Code example provided in Rust) ### Response #### Success Response (200) - **access_token** (string) - The access token issued by the authorization server (in URL fragment). - **token_type** (string) - The type of the token (e.g., 'Bearer'). - **expires_in** (number) - The lifetime in seconds of the access token. - **scope** (string, optional) - The scope of the access token, if issued. #### Response Example (Token is appended to the redirect URI as a URL fragment, e.g., `http://localhost:8080/callback#access_token=SlAV32hkYJ_1j8_aQ7aX&token_type=Bearer&expires_in=3600`) ``` -------------------------------- ### Update Custom HTTP Client to Use http::Request/Response Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md The `HttpRequest` and `HttpResponse` types in `oauth2-rs` are now aliases for `http::Request` and `http::Response`. Custom HTTP client implementations need to be updated to use these standard types from the `http` crate. ```Rust use http::Request; use http::Response; // Previously: use oauth2::{HttpRequest, HttpResponse}; // Custom client implementation using http::Request and http::Response ``` -------------------------------- ### Add Extra Parameters to OAuth2 Requests in Rust Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Illustrates how to append custom parameters to authorization URLs and token exchange requests. This is useful for provider-specific extensions like audience or prompt settings. ```rust let (auth_url, csrf_token) = client .authorize_url(CsrfToken::new_random) .add_scope(Scope::new("openid".to_string())) .add_extra_param("prompt", "consent") .add_extra_param("access_type", "offline") .url(); let token = client .exchange_client_credentials() .add_extra_param("audience", "https://api.example.com") .request(&http_client) .unwrap(); ``` -------------------------------- ### Resource Owner Password Credentials Grant Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Explains the Resource Owner Password Credentials Grant flow, used for exchanging username and password directly for tokens. Recommended only when other flows are not feasible. ```APIDOC ## Resource Owner Password Credentials Grant ### Description Exchanges username and password directly for tokens. Only use when other flows are not possible. ### Method POST ### Endpoint (Implicitly handled by `oauth2` crate, typically to the Token URL) ### Parameters (Configuration parameters are set on the `BasicClient`) ### Request Example ```json { "grant_type": "password", "username": "user@example.com", "password": "hunter2", "scope": "read" } ``` ### Response #### Success Response (200) - **access_token** (string) - The access token issued by the authorization server. - **token_type** (string) - The type of the token (e.g., 'Bearer'). - **expires_in** (number) - The lifetime in seconds of the access token. - **refresh_token** (string, optional) - The refresh token, if issued. - **scope** (string, optional) - The scope of the access token, if issued. #### Response Example ```json { "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Q92uErx2Y", "scope": "read" } ``` ``` -------------------------------- ### POST /token (Refresh Token) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Exchanges a valid refresh token for a new access token to maintain session persistence. ```APIDOC ## POST /token ### Description Exchanges a refresh token for a new access token. ### Method POST ### Endpoint /token ### Request Body - **grant_type** (string) - Required - Must be 'refresh_token' - **refresh_token** (string) - Required - The refresh token string - **scope** (string) - Optional - Requested scopes ### Request Example { "grant_type": "refresh_token", "refresh_token": "stored_refresh_token", "scope": "read" } ### Response #### Success Response (200) - **access_token** (string) - The new access token - **refresh_token** (string) - Optional new refresh token #### Response Example { "access_token": "new_access_token_value", "refresh_token": "new_refresh_token_value" } ``` -------------------------------- ### Enable reqwest-blocking Feature Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md To use the synchronous (blocking) `reqwest` HTTP client in `oauth2-rs`, the `reqwest-blocking` feature must be explicitly enabled in `Cargo.toml`. Previously, the default `reqwest` feature included blocking capabilities. ```toml [dependencies.oauth2] version = "5" features = ["reqwest-blocking"] ``` -------------------------------- ### Rust: Refresh Token Exchange with OAuth2.rs Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Exchanges a stored refresh token for a new access token. This is useful for maintaining user authentication without requiring them to re-authorize frequently. It uses the `oauth2-rs` crate and `reqwest` for HTTP communication. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, ClientId, ClientSecret, RefreshToken, Scope, TokenResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let refresh_token = RefreshToken::new("stored_refresh_token".to_string()); let token_result = client .exchange_refresh_token(&refresh_token) .add_scope(Scope::new("read".to_string())) .request(&http_client) .unwrap(); println!("New access token: {}", token_result.access_token().secret()); if let Some(new_refresh_token) = token_result.refresh_token() { println!("New refresh token: {}", new_refresh_token.secret()); } ``` -------------------------------- ### Resource Owner Password Credentials Grant Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Shows the Resource Owner Password Credentials Grant flow, which exchanges a username and password directly for tokens. This flow should only be used when other OAuth2 flows are not possible due to security implications. It involves setting up a BasicClient and using `exchange_password()` with user credentials. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthUrl, ClientId, ClientSecret, ResourceOwnerPassword, ResourceOwnerUsername, Scope, TokenResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let token_result = client .exchange_password( &ResourceOwnerUsername::new("user@example.com".to_string()), &ResourceOwnerPassword::new("hunter2".to_string()), ) .add_scope(Scope::new("read".to_string())) .request(&http_client) .unwrap(); println!("Access token: {}", token_result.access_token().secret()); ``` -------------------------------- ### Revoke Access and Refresh Tokens in Rust Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates how to revoke OAuth2 tokens using the BasicClient. It requires a configured revocation URL and a blocking reqwest client to perform the request. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AccessToken, AuthUrl, ClientId, ClientSecret, RefreshToken, RevocationUrl, StandardRevocableToken, TokenResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()) .set_revocation_url( RevocationUrl::new("https://auth.example.com/revoke".to_string()).unwrap(), ); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let token_to_revoke: StandardRevocableToken = AccessToken::new("access_token_to_revoke".to_string()).into(); client .revoke_token(token_to_revoke) .unwrap() .request(&http_client) .unwrap(); ``` -------------------------------- ### Error Handling in OAuth2 Operations Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Demonstrates how to handle different types of errors that can occur during OAuth2 operations, such as server responses, HTTP request failures, and parsing issues. ```APIDOC ## Error Handling Handle different error types returned by OAuth2 operations. ### Method ```rust // Example code demonstrating error handling use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AuthorizationCode, AuthUrl, ClientId, ClientSecret, RequestTokenError, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .unwrap(); let result = client .exchange_code(AuthorizationCode::new("invalid_code".to_string())) .request(&http_client); match result { Ok(token) => { println!("Token received: {}", token.access_token().secret()); } Err(RequestTokenError::ServerResponse(err)) => { // OAuth2 error response from server println!("Server error: {:?}", err.error()); if let Some(desc) = err.error_description() { println!("Description: {}", desc); } } Err(RequestTokenError::Request(err)) => { // HTTP request failed println!("HTTP request error: {}", err); } Err(RequestTokenError::Parse(err, body)) => { // Failed to parse response println!("Parse error: {}, body: {:?}", err, body); } Err(RequestTokenError::Other(msg)) => { // Other error println!("Other error: {}", msg); } } ``` ``` -------------------------------- ### Rust: Token Introspection (RFC 7662) with OAuth2.rs Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Queries the authorization server for metadata about a given token. This is used to validate token status and retrieve associated information like active status, scopes, and user details. Implemented using `oauth2-rs` and `reqwest`. ```rust use oauth2::basic::BasicClient; use oauth2::reqwest; use oauth2::{ AccessToken, AuthUrl, ClientId, ClientSecret, IntrospectionUrl, TokenIntrospectionResponse, TokenUrl, }; let client = BasicClient::new(ClientId::new("client_id".to_string())) .set_client_secret(ClientSecret::new("client_secret".to_string())) .set_auth_uri(AuthUrl::new("https://auth.example.com/authorize".to_string()).unwrap()) .set_token_uri(TokenUrl::new("https://auth.example.com/token".to_string()).unwrap()) .set_introspection_url( IntrospectionUrl::new("https://auth.example.com/introspect".to_string()).unwrap(), ); let http_client = reqwest::blocking::ClientBuilder::new() .redirect(reqwest::redirect::Policy::none()) .build() .expect("Client should build"); let token_to_introspect = AccessToken::new("token_to_check".to_string()); let introspection_result = client .introspect(&token_to_introspect) .set_token_type_hint("access_token") .request(&http_client) .unwrap(); println!("Token active: {}", introspection_result.active()); if let Some(username) = introspection_result.username() { println!("Username: {}", username); } if let Some(scopes) = introspection_result.scopes() { println!("Scopes: {:?}", scopes); } if let Some(exp) = introspection_result.exp() { println!("Expires: {}", exp); } ``` -------------------------------- ### Adding Typestate Generic Parameters to Client Source: https://github.com/ramosbugs/oauth2-rs/blob/main/UPGRADE.md This snippet demonstrates how to update a custom Client type definition to include the five required generic typestate parameters. The use of default values (EndpointNotSet) simplifies instantiation. ```rust type SpecialClient = Client; ``` -------------------------------- ### POST /introspect (Token Introspection) Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Queries the authorization server to validate a token and retrieve its metadata. ```APIDOC ## POST /introspect ### Description Query the authorization server for metadata about a token. ### Method POST ### Endpoint /introspect ### Request Body - **token** (string) - Required - The token to introspect - **token_type_hint** (string) - Optional - 'access_token' or 'refresh_token' ### Request Example { "token": "token_to_check", "token_type_hint": "access_token" } ### Response #### Success Response (200) - **active** (boolean) - Whether the token is currently active - **username** (string) - Associated username - **scope** (string) - Associated scopes - **exp** (integer) - Expiration timestamp #### Response Example { "active": true, "username": "user1", "scope": "read", "exp": 1672531200 } ``` -------------------------------- ### Client Credentials Grant Source: https://context7.com/ramosbugs/oauth2-rs/llms.txt Details the Client Credentials Grant flow, suitable for server-to-server authentication where the application acts on its own behalf. ```APIDOC ## Client Credentials Grant ### Description Used for server-to-server authentication where the application acts on its own behalf. ### Method POST ### Endpoint (Implicitly handled by `oauth2` crate, typically to the Token URL) ### Parameters (Configuration parameters are set on the `BasicClient`) ### Request Example ```json { "grant_type": "client_credentials", "scope": "api:read" } ``` ### Response #### Success Response (200) - **access_token** (string) - The access token issued by the authorization server. - **token_type** (string) - The type of the token (e.g., 'Bearer'). - **expires_in** (number) - The lifetime in seconds of the access token. - **scope** (string, optional) - The scope of the access token, if issued. #### Response Example ```json { "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "Bearer", "expires_in": 3600, "scope": "api:read" } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.