rustauth-oauth-provider 0.3.0

OAuth 2.1 and OpenID Connect provider support for RustAuth.
Documentation
use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::{Map, Value};
use time::OffsetDateTime;

use crate::models::SchemaClient;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenRequest {
    pub grant_type: Option<String>,
    pub client_id: Option<String>,
    pub client_secret: Option<String>,
    pub code: Option<String>,
    pub code_verifier: Option<String>,
    pub redirect_uri: Option<String>,
    pub refresh_token: Option<String>,
    #[serde(default, deserialize_with = "deserialize_resource")]
    pub resource: Vec<String>,
    pub scope: Option<String>,
}

fn deserialize_resource<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
    D: Deserializer<'de>,
{
    let value = Option::<Value>::deserialize(deserializer)?;
    Ok(match value {
        Some(Value::String(resource)) if !resource.is_empty() => vec![resource],
        Some(Value::String(_)) => return Err(D::Error::custom("resource must not be empty")),
        Some(Value::Array(resources)) => resources
            .into_iter()
            .map(|resource| match resource {
                Value::String(resource) if !resource.is_empty() => Ok(resource),
                _ => Err(D::Error::custom(
                    "resource must be a string or array of strings",
                )),
            })
            .collect::<Result<Vec<_>, _>>()?,
        Some(_) => {
            return Err(D::Error::custom(
                "resource must be a string or array of strings",
            ))
        }
        _ => Vec::new(),
    })
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenResponse {
    pub access_token: String,
    pub expires_in: i64,
    pub expires_at: i64,
    pub token_type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub refresh_token: Option<String>,
    pub scope: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id_token: Option<String>,
    #[serde(flatten)]
    pub extra: Map<String, Value>,
}

#[derive(Debug, Clone)]
pub(crate) struct ValidatedAccessToken {
    pub active: bool,
    pub claims: Value,
    pub user_id: Option<String>,
    pub client_id: Option<String>,
    pub scopes: Vec<String>,
}

#[derive(Debug, Clone)]
pub(crate) struct ValidatedIdTokenHint {
    pub client: SchemaClient,
    pub session_id: String,
}

#[derive(Debug, Clone)]
pub(crate) struct RefreshTokenGrantInput<'a> {
    pub client_id: &'a str,
    pub client_secret: Option<&'a str>,
    pub refresh_token: &'a str,
    pub requested_scopes: Vec<String>,
    pub resource: Vec<String>,
}

#[derive(Debug, Clone)]
pub(super) struct AccessTokenInput {
    pub(super) user_id: Option<String>,
    pub(super) session_id: Option<String>,
    pub(super) reference_id: Option<String>,
    pub(super) scopes: Vec<String>,
    pub(super) machine_to_machine: bool,
    pub(super) resource: Vec<String>,
}

#[derive(Debug, Clone, Copy)]
pub(super) struct IdTokenInput<'a> {
    pub(super) user_id: &'a str,
    pub(super) session_id: Option<&'a str>,
    pub(super) scopes: &'a [String],
    pub(super) nonce: Option<&'a str>,
    pub(super) auth_time: Option<OffsetDateTime>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct AuthorizationCodeValue {
    pub client_id: String,
    pub redirect_uri: Option<String>,
    pub scopes: Vec<String>,
    pub user_id: String,
    pub session_id: String,
    pub reference_id: Option<String>,
    pub nonce: Option<String>,
    pub code_challenge: Option<String>,
    pub code_challenge_method: Option<String>,
    #[serde(default)]
    pub auth_time: Option<OffsetDateTime>,
}