oauth 0.0.2

Universal OAuth 2.0 adapter for Rust web frameworks
Documentation
use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate::config::OAuthConfig;
use crate::error::{OAuthError, Result};

pub mod authorization_code;
pub mod client_credentials;
pub mod refresh_token;

/// Supported OAuth 2.0 grant types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum GrantType {
    /// Client credentials grant.
    ClientCredentials,
    /// Authorization code grant.
    AuthorizationCode,
    /// Refresh token grant.
    RefreshToken,
}

/// Parsed token endpoint request payload.
#[derive(Debug, Clone, Deserialize)]
pub struct TokenRequest {
    pub grant_type: GrantType,
    #[serde(default)]
    pub client_id: Option<String>,
    #[serde(default)]
    pub client_secret: Option<String>,
    #[serde(default)]
    pub scope: Option<String>,
    #[serde(default)]
    pub code: Option<String>,
    #[serde(default)]
    pub redirect_uri: Option<String>,
    #[serde(default)]
    pub code_verifier: Option<String>,
    #[serde(default)]
    pub refresh_token: Option<String>,
}

/// Standard OAuth token response.
#[derive(Debug, Clone, Serialize)]
pub struct TokenResponse {
    pub access_token: String,
    pub token_type: String,
    pub expires_in: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub refresh_token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scope: Option<String>,
}

impl TokenResponse {
    /// Construct a bearer token response.
    pub fn bearer(
        access_token: String,
        ttl: Duration,
        refresh_token: Option<String>,
        scope: Option<String>,
    ) -> Self {
        Self {
            access_token,
            token_type: "Bearer".into(),
            expires_in: ttl.as_secs(),
            refresh_token,
            scope,
        }
    }
}

/// Dispatch an incoming token request to the correct grant handler.
pub fn issue_token(config: &OAuthConfig, request: TokenRequest) -> Result<TokenResponse> {
    match request.grant_type {
        GrantType::ClientCredentials => client_credentials::issue_token(config, &request),
        GrantType::AuthorizationCode => authorization_code::issue_token(config, &request),
        GrantType::RefreshToken => refresh_token::issue_token(config, &request),
    }
}