diff --git a/README.md b/README.md index 36ac79a..cfa05da 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ ![GitHub License](https://img.shields.io/github/license/nakaryo716/tiny_google_oidc) Tiny library for Google's OpenID Connect. -Implementation in server flow +This library provides essential tools for handling Google's OpenID Connect flow, including +generating authentication URLs, verifying tokens. +Implementation in server flow. [google document](https://developers.google.com/identity/openid-connect/openid-connect) ## Feature - Generate a CSRF Token @@ -13,13 +15,6 @@ Implementation in server flow - Verify CSRF token and retrieve id_token - Exchange code for id_token (using reqwest) - Decode id_token (Base64URLDecode) to get user information -- Refresh access token using refresh token (using reqwest) -- Revoke access/refresh token (using reqwest) -## Caution -This library is designed for direct communication with Google over HTTPS. -It does not validate the id_token when converting it to a JWT, -so the id_token cannot be passed to other components of your app. -[See document](https://developers.google.com/identity/openid-connect/openid-connect#obtainuserinfo) ## Example Here's hou you can use this library with the axum framework. ### 1. Create Config diff --git a/src/code.rs b/src/code.rs index 50093a1..17cfebc 100644 --- a/src/code.rs +++ b/src/code.rs @@ -20,8 +20,9 @@ //! - This is obtained after validating the `RawCodeResponse` with a CSRF token. //! //! # Examples -//! ## Generating an Authorization Request URL -//! ```rust,no_run +//! Generating an Authorization Request URL +//! ```rust, no_run +//! # use tiny_google_oidc::{config::Config, csrf_token::CSRFToken, nonce::Nonce, code::{CodeRequest, AdditionalScope, AccessType}}; //! let config = Config::builder() //! .client_id("your_client_id") //! .redirect_uri("your_redirect_uri") @@ -29,22 +30,14 @@ //! //! let csrf_token = CSRFToken::new().unwrap(); //! let nonce = Nonce::new(); +//! let access_type = AccessType::Online; //! let scope = AdditionalScope::Email; //! -//! let request = CodeRequest::new(true, &config, scope, &csrf_token, &nonce); +//! let request = CodeRequest::new(access_type, &config, scope, &csrf_token, &nonce); //! let url = request.try_into_url().unwrap(); //! println!("Auth URL: {}", url); //! ``` //! -//! ## Handling the Callback and Verifying the Authorization Code -//! ```rust,no_run -//! let response = RawCodeResponse::new(req).unwrap(); -//! // get stored CSRF token From DB(Redis, in memory ...) -//! let csrf_token = store.get("csrf_token_key")?; -//! -//! let code = response.exchange_with_code(csrf_token).expect("CSRF token mismatch!"); -//! ``` -//! //! # Flow //! 1. Generate a CSRF token (`CSRFToken`) and include it in the authorization request. //! 2. Redirect the user to Google's authentication page. @@ -70,23 +63,10 @@ use std::{collections::HashMap, iter::Iterator}; /// /// # Purpose /// The `Code` is used to construct an `IDTokenRequest`, which is required to retrieve an ID token from Google. -/// ```rust,no_run -/// let code: Code = Code::new_with_verify_csrf(res, stored_csrf_token)?; -/// let id_token_req = IDTokenRequest::new(&config, code); -/// ``` -/// /// # How to Create /// A `Code` can only be created after validating the `CSRFToken`. /// Use either `Code::new_with_verify_csrf` or `RawCodeResponse::exchange_with_code` to validate and create a `Code`. /// -/// # Example -/// ```rust,no_run -/// let response = RawCodeResponse::new(req).unwrap(); -/// let csrf_token = store.get("csrf_token_key")?; -/// -/// let code = response.exchange_with_code(csrf_token).expect("CSRF token mismatch!"); -/// ``` -/// /// # Notes /// - Always validate the `CSRFToken` before using the `Code`. /// - The `Code` is essential for constructing an `IDTokenRequest` to retrieve an ID token. @@ -107,7 +87,8 @@ impl Code { /// Generates a URL to initiate the authorization request. /// # Example -/// ```rust,no_run +/// ```rust, no_run +/// # use tiny_google_oidc::{config::Config, csrf_token::CSRFToken, nonce::Nonce, code::{AdditionalScope, AccessType, CodeRequest}}; /// let config = Config::builder() /// .client_id("your_client_id") /// .redirect_uri("your_redirect_uri") @@ -119,7 +100,6 @@ impl Code { /// /// let request = CodeRequest::new(AccessType::Online, &config, scope, &csrf_token, &nonce); /// let url = request.try_into_url().unwrap(); -/// println!("Auth URL: {}", url); /// ``` #[derive(Debug, Clone)] pub struct CodeRequest<'a> { @@ -203,13 +183,6 @@ impl<'a> CodeRequest<'a> { /// A response from Google containing an unverified authorization code and state. /// Must be validated using a CSRF token before use. -/// # Example -/// ```rust,no_run -/// let response = RawCodeResponse::new(req).unwrap(); -/// let csrf_token = store.get("csrf_token_key")?; -/// -/// let code = response.exchange_with_code(csrf_token).expect("CSRF token mismatch!"); -/// ``` #[derive(Debug, Clone)] pub struct RawCodeResponse { state: RawCSRFToken, @@ -305,17 +278,6 @@ pub enum AccessType { /// /// ## None /// No additional scopes are added -/// -/// # Example -/// ```rust,no_run -/// use crate::code::AdditionalScope; -/// -/// let additional_scopes = AdditionalScope::Both; -/// let request = CodeRequest::new(true, &config, additional_scopes, &csrf_token, &nonce); -/// let url = request.into_url().unwrap(); -/// println!("Authorization URL: {}", url); -/// ``` -/// /// If **no additional scopes** are specified, the request will **only include `openid`**, which is required for authentication. /// /// # Notes diff --git a/src/config.rs b/src/config.rs index 2ca99dd..c451b63 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,7 +9,7 @@ //! //! # Example //! ```rust -//! use tny_google_oidc::config::Config; +//! # use tiny_google_oidc::config::Config; //! //! let config = Config::builder() //! .auth_endpoint("https://accounts.google.com/o/oauth2/auth") @@ -172,7 +172,7 @@ impl ConfigBuilder { /// use tiny_google_oidc::config::AuthEndPoint; /// let endpoint_str = "https://accounts.google.com/o/oauth2/auth"; /// let auth_endpoint: AuthEndPoint = endpoint_str.into(); -/// assert_eq!(auth_endpoint, AuthEndPoint(endpoint_str.to_string())) +/// assert_eq!(auth_endpoint.value(), endpoint_str) /// ``` #[derive(Debug, Clone, Default, PartialEq)] pub struct AuthEndPoint(pub(crate) String); @@ -215,7 +215,7 @@ impl From for AuthEndPoint { /// /// let client_id_str = "your-client-id"; /// let client_id: ClientID = client_id_str.into(); -/// assert_eq!(client_id, ClientID(client_id_str.to_string())); +/// assert_eq!(client_id.value(), client_id_str); /// ``` #[derive(Debug, Clone, Default, PartialEq)] pub struct ClientID(pub(crate) String); @@ -258,7 +258,7 @@ impl From for ClientID { /// /// let secret_str = "your-client-secret"; /// let client_secret: ClientSecret = secret_str.into(); -/// assert_eq!(client_secret, ClientSecret(secret_str.to_string())); +/// assert_eq!(client_secret.value(), secret_str); /// ``` #[derive(Debug, Clone, Default, PartialEq)] pub struct ClientSecret(pub(crate) String); @@ -300,7 +300,7 @@ impl From for ClientSecret { /// /// let token_endpoint_str = "https://oauth2.googleapis.com/token"; /// let token_endpoint: TokenEndPoint = token_endpoint_str.into(); -/// assert_eq!(token_endpoint, TokenEndPoint(token_endpoint_str.to_string())); +/// assert_eq!(token_endpoint.value(), token_endpoint_str); /// ``` #[derive(Debug, Clone, Default, PartialEq)] pub struct TokenEndPoint(pub(crate) String); @@ -343,7 +343,7 @@ impl From for TokenEndPoint { /// /// let redirect_uri_str = "https://your-app.com/callback"; /// let redirect_uri: RedirectURI = redirect_uri_str.into(); -/// assert_eq!(redirect_uri, RedirectURI(redirect_uri_str.to_string())); +/// assert_eq!(redirect_uri.value(), redirect_uri_str); /// ``` #[derive(Debug, Clone, Default, PartialEq)] pub struct RedirectURI(pub(crate) String); diff --git a/src/easy.rs b/src/easy.rs index b6d4dbe..40a74da 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -47,12 +47,16 @@ use crate::{ /// Returns an error if token generation or URI construction fails. /// /// # Examples -/// ```no_run -/// let (csrf_token, nonce, uri) = generate_auth_redirect( -/// &config, -/// AccessType::Offline, -/// AdditionalScope::Both -/// )?; +/// ```rust +/// # use tiny_google_oidc::{config::Config, code::{AdditionalScope, AccessType}, easy::generate_auth_redirect}; +/// # fn doc(config: Config) -> Result<(), Box> { +/// let (csrf_token, nonce, uri) = generate_auth_redirect( +/// &config, +/// AccessType::Offline, +/// AdditionalScope::Both +/// )?; +/// Ok(()) +/// # } /// ``` pub fn generate_auth_redirect( config: &Config, @@ -88,15 +92,17 @@ pub fn generate_auth_redirect( /// Returns an error if CSRF validation fails or if the query cannot be parsed. /// /// # Examples -/// ```no_run -/// fn callback_handler(config: &Config, stored_csrf_token: &str, req: Request) -> Result<(), Error> { +/// ```rust +/// # use tiny_google_oidc::{easy::create_id_token_request, config::Config}; +/// # fn callback_handler(config: &Config, stored_csrf_token: &str, req: http::Request) -> Result<(), Box> { /// // http::Request is implemented QueryExtractor trait /// let id_token_req = create_id_token_request( /// config, /// stored_csrf_token, /// req /// )?; -/// } +/// # Ok(()) +/// # } /// ``` pub fn create_id_token_request<'a, Q: QueryExtractor>( config: &'a Config, diff --git a/src/lib.rs b/src/lib.rs index 98f535b..e5493b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! Tiny library for Google's OpenID Connect. //! //! This library provides essential tools for handling Google's OpenID Connect flow, including -//! generating authentication URLs, verifying tokens, and managing access/refresh tokens. +//! generating authentication URLs, verifying tokens. //! Implementation in server flow. //! [google document](https://developers.google.com/identity/openid-connect/openid-connect) //! # Feature @@ -10,15 +10,6 @@ //! - Verify CSRF token and retrieve id_token //! - Exchange code for id_token (using reqwest) //! - Decode id_token (Base64URLDecode) to get user information -//! - Refresh access token using refresh token (using reqwest) -//! - Revoke access/refresh token (using reqwest) -//! # Caution -//! - This library is designed for direct communication with Google over HTTPS. -//! - It does **not** validate the `id_token` when converting it to a JWT. As a result, the `id_token` -//! should not be passed to other components of your application. -//! - For more details, refer to the -//! -//! [Google OpenID Connect documentation](https://developers.google.com/identity/openid-connect/openid-connect#obtainuserinfo). //! # Examples //! For example usage, see the [examples directory](https://github.com/nakaryo716/tiny_google_oidc.git). pub mod code; diff --git a/src/nonce.rs b/src/nonce.rs index c9cb123..b53613b 100644 --- a/src/nonce.rs +++ b/src/nonce.rs @@ -6,30 +6,28 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Nonce(pub(crate) String); -/// # **Overview** +/// # Overview /// A `Nonce` is a **unique, random value** used to prevent replay attacks in OpenID Connect authentication. /// It ensures that the authentication request and response belong to the same session. /// /// This structure automatically generates a new random nonce using `UUIDv4` when created. /// -/// # **Usage** -/// +/// # Usage /// - The nonce is included in the authentication request (`CodeRequest`). /// - When receiving an ID token from Google, the `Nonce` in the token should be verified /// against the original nonce sent in the request. /// -/// # **Implementation Details** -/// +/// # Implementation Details /// - The `Nonce` value is a **UUIDv4 string**. /// - It implements `Serialize` and `Deserialize` for easy integration with JSON-based flows. /// -/// # **Example** +/// # Example /// /// ```rust,no_run -/// use crate::nonce::Nonce; +/// use tiny_google_oidc::nonce::Nonce; /// /// let nonce = Nonce::new(); -/// println!("Generated Nonce: {}", nonce.0); +/// println!("Generated Nonce: {:?}", nonce); /// ``` /// - Always **verify the nonce** in the received ID token against the originally generated value /// to ensure security.