Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@
![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
- Generate an authentication request URL (code) for Google
- 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
Expand Down
52 changes: 7 additions & 45 deletions src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,24 @@
//! - 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")
//! .build();
//!
//! 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.
Expand All @@ -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.
Expand All @@ -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")
Expand All @@ -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> {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -215,7 +215,7 @@ impl From<String> 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);
Expand Down Expand Up @@ -258,7 +258,7 @@ impl From<String> 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);
Expand Down Expand Up @@ -300,7 +300,7 @@ impl From<String> 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);
Expand Down Expand Up @@ -343,7 +343,7 @@ impl From<String> 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);
Expand Down
24 changes: 15 additions & 9 deletions src/easy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn std::error::Error>> {
/// let (csrf_token, nonce, uri) = generate_auth_redirect(
/// &config,
/// AccessType::Offline,
/// AdditionalScope::Both
/// )?;
/// Ok(())
/// # }
/// ```
pub fn generate_auth_redirect(
config: &Config,
Expand Down Expand Up @@ -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<T>(config: &Config, stored_csrf_token: &str, req: http::Request<T>) -> Result<(), Box<dyn std::error::Error>> {
/// // 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,
Expand Down
11 changes: 1 addition & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand Down
14 changes: 6 additions & 8 deletions src/nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading