raos 0.0.2

Async Rust implementation of the OAuth 2.1 Authorization Server
Documentation
use crate::{
    authorize::{AuthorizationProvider, GrantAuthorizationResult},
    common::model::{Client, ClientProvider, CodeChallenge, Grant},
    manager::OAuthManager,
    token::{GrantType, RefreshGrant, Token, TokenProvider},
};
use async_trait::async_trait;
use std::{
    string::ToString,
    sync::LazyLock,
    time::{Duration, Instant},
};

static DOCTEST_CLIENT: LazyLock<Client> = LazyLock::new(|| Client {
    client_id: "CLIENT_ID".to_string(),
    redirect_uris: vec!["https://example.com".to_string()],
    confidential: false,
});

/// Mock function to return an owner ID used in other tests
pub fn owner_id_from_session() -> u32 {
    1
}

/// Mock function to return an oauth manager with basic data
pub fn oauth_manager_from_application_state() -> OAuthManager<u32, (), ()> {
    OAuthManager::builder()
        .client_provider(DocTestClientProvider)
        .authorization_provider(DocTestAuthorizationProvider)
        .token_provider(DocTestTokenProvider)
        .build()
}

struct DocTestClientProvider;

#[async_trait]
impl ClientProvider for DocTestClientProvider {
    type Error = ();

    async fn get_client_by_id(&self, client_id: &str) -> Result<Option<Client>, Self::Error> {
        if client_id == DOCTEST_CLIENT.client_id {
            Ok(Some(DOCTEST_CLIENT.clone()))
        } else {
            Ok(None)
        }
    }

    async fn allow_client_scopes(
        &self,
        _client: &Client,
        requested_scopes: Vec<String>,
    ) -> Result<Vec<String>, Self::Error> {
        Ok(requested_scopes)
    }

    async fn verify_client_secret(
        &self,
        _client: &Client,
        client_secret: &str,
    ) -> Result<bool, Self::Error> {
        Ok(client_secret == "CLIENT_SECRET")
    }
}

struct DocTestAuthorizationProvider;

#[async_trait]
impl AuthorizationProvider for DocTestAuthorizationProvider {
    type OwnerId = u32;
    type Extras = ();
    type Error = ();

    async fn authorize_grant(
        &self,
        _client: &Client,
        _scopes: &[String],
        _extras: &mut Option<Self::Extras>,
    ) -> Result<GrantAuthorizationResult<Self::OwnerId>, Self::Error> {
        Ok(GrantAuthorizationResult::Authorized(1))
    }

    async fn generate_code_for_grant(
        &self,
        _grant: Grant<Self::OwnerId>,
    ) -> Result<String, Self::Error> {
        Ok("AUTHORIZATION_CODE".to_string())
    }

    async fn exchange_code_for_grant(
        &self,
        code: String,
    ) -> Result<Option<Grant<Self::OwnerId>>, Self::Error> {
        if code == "AUTHORIZATION_CODE" {
            Ok(Some(Grant {
                owner_id: 1,
                client_id: "CLIENT_ID".to_string(),
                scope: vec!["SCOPE".to_string()],
                redirect_uri: "https://example.com".parse().unwrap(),
                code_challenge: CodeChallenge::Plain {
                    code_challenge: "CODE_CHALLENGE".to_string(),
                },
            }))
        } else {
            Ok(None)
        }
    }
}

struct DocTestTokenProvider;

#[async_trait]
impl TokenProvider for DocTestTokenProvider {
    type OwnerId = u32;
    type Error = ();

    async fn token(
        &self,
        _client: &Client,
        _grant: GrantType<Self::OwnerId>,
    ) -> Result<Token, Self::Error> {
        Ok(Token {
            token: "ACCESS_TOKEN".to_string(),
            refresh_token: Some("REFRESH_TOKEN".to_string()),
            valid_until: Instant::now() + Duration::from_secs(3600),
        })
    }

    async fn exchange_refresh_token(
        &self,
        _refresh_token: String,
    ) -> Result<Option<RefreshGrant<Self::OwnerId>>, Self::Error> {
        if _refresh_token == "REFRESH_TOKEN" {
            Ok(Some(RefreshGrant {
                client_id: "CLIENT_ID".to_string(),
                resource_owner: 1,
                scope: vec!["SCOPE".to_string()],
            }))
        } else {
            Ok(None)
        }
    }
}