pas-external 3.0.0

Ppoppo Accounts System (PAS) external SDK -- OAuth2 PKCE, PASETO verification, Axum middleware, session liveness
Documentation
//! [`PasAuth`] — the single public entry point that wires a PAS OAuth2 router
//! together with a matching [`SessionResolver`].
//!
//! Construct one per application, then mount the router and share the resolver
//! with whatever custom Axum middleware needs to read the authenticated
//! context. Both halves share the same [`SessionStore`] and cookie name by
//! construction, so the "write side" (OAuth callback) and the "read side"
//! (auth middleware) cannot drift out of sync.

use std::sync::Arc;

use axum::Router;

use super::config::PasAuthConfig;
use super::routes;
use super::session::SessionResolver;
use super::state::AuthState;
use super::traits::{AccountResolver, SessionStore};

/// Bundle of the PAS auth router + a matching [`SessionResolver`].
///
/// # Example
///
/// ```rust,ignore
/// // Single concrete adapter implementing both traits — share one Arc.
/// let adapter = Arc::new(PasAdapter::new(pool));
/// let pas_auth = PasAuth::new(pas_config, Arc::clone(&adapter), adapter);
///
/// let resolver = pas_auth.resolver();       // clone into AppState
/// let router   = pas_auth.into_router();    // merge into main router
/// ```
pub struct PasAuth<S: SessionStore> {
    router: Router,
    resolver: SessionResolver<S>,
}

impl<S: SessionStore> PasAuth<S> {
    /// Assemble the PAS auth router and a matching [`SessionResolver`].
    ///
    /// Both trait objects are taken as [`Arc`] so that consumers can choose
    /// to share a single backing instance (by passing the same `Arc` twice
    /// when one concrete type implements both [`AccountResolver`] and
    /// [`SessionStore`]) or keep them independent. The router write side and
    /// the resolver read side always share the same `Arc<S>` for the session
    /// store, so the cookie name and the store they resolve against cannot
    /// drift.
    pub fn new<U: AccountResolver>(
        config: PasAuthConfig,
        account_resolver: Arc<U>,
        session_store: Arc<S>,
    ) -> Self {
        let cookie_name: Arc<str> = Arc::from(config.settings.session_cookie_name.as_str());

        let state = AuthState {
            client: Arc::new(config.client),
            account_resolver,
            session_store: Arc::clone(&session_store),
            settings: config.settings,
        };

        let router = routes::build_router(state);
        let resolver = SessionResolver::new(session_store, cookie_name);

        Self { router, resolver }
    }

    /// Cheap clone of the session resolver handle.
    #[must_use]
    pub fn resolver(&self) -> SessionResolver<S> {
        self.resolver.clone()
    }

    /// Consume the bundle and return the router.
    pub fn into_router(self) -> Router {
        self.router
    }
}