pas-external 0.6.0

Ppoppo Accounts System (PAS) external SDK — OAuth2 PKCE, JWT verification port, Axum middleware, session liveness
Documentation
use axum::extract::FromRequestParts;
use axum::http::StatusCode;

use crate::types::{PpnumId, SessionId, UserId};

/// Minimal authenticated identity from PAS.
///
/// Consumers can use this as their `SessionStore::AuthContext` if they
/// don't need richer auth context (e.g., roles, tenancy).
///
/// For consumers that need more context, implement `SessionStore::AuthContext`
/// with your own type.
///
/// Can be used as an Axum extractor when inserted into request extensions.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AuthPpnum {
    /// Session ID (from cookie).
    pub session_id: SessionId,
    /// App-specific user ID (from `SessionStore::find`).
    pub user_id: UserId,
    /// PAS ppnum_id (immutable ULID, = OAuth `sub` claim).
    pub ppnum_id: PpnumId,
}

impl AuthPpnum {
    /// Create a new `AuthPpnum`.
    ///
    /// Use this in your [`SessionStore::find`](super::SessionStore::find) implementation
    /// when using `AuthPpnum` as your `AuthContext` type.
    #[must_use]
    pub fn new(session_id: SessionId, user_id: UserId, ppnum_id: PpnumId) -> Self {
        Self {
            session_id,
            user_id,
            ppnum_id,
        }
    }
}

impl<S: Send + Sync> FromRequestParts<S> for AuthPpnum {
    type Rejection = StatusCode;

    async fn from_request_parts(
        parts: &mut axum::http::request::Parts,
        _state: &S,
    ) -> Result<Self, Self::Rejection> {
        parts
            .extensions
            .get::<AuthPpnum>()
            .cloned()
            .ok_or(StatusCode::UNAUTHORIZED)
    }
}