ppoppo-sdk-core 0.4.1

Internal shared primitives for the Ppoppo SDK family (pas-external, pas-plims, pcs-external) — verifier port, audit trait, session liveness port, OIDC discovery, perimeter Bearer-auth Layer kit, identity types. Not a stable public API; do not depend on this crate directly. Consume the SDK crates that re-export from it (e.g. `pas-external`).
Documentation
//! [`BearerAuthConfig`] — per-consumer perimeter configuration
//! (cookie source + clearance closure + pre-auth path exemptions).

use std::sync::Arc;

use axum_extra::extract::cookie::CookieJar;

/// Per-consumer perimeter configuration carried into
/// [`super::BearerAuthLayer`].
///
/// Everything the perimeter needs that is *consumer-specific*:
/// - the fallback bearer cookie name ([`Self::access_cookie_name`]),
/// - the dead-session cookie-clearance closure ([`Self::on_clear`]),
/// - the pre-auth path allowlist ([`Self::path_exempt`]).
///
/// The Layer uses [`Self::access_cookie_name`] as the fallback bearer
/// source (when `Authorization` is absent) and the [`Self::on_clear`]
/// closure to mint Set-Cookie removals on dead-session 401s. Cookie
/// names are domain-scoped per RFC 6265bis; each consumer owns its
/// `__Host-*_at` literal in its own `cookies` module.
#[derive(Clone)]
pub struct BearerAuthConfig {
    /// Cookie name carrying the Bearer token in browser contexts —
    /// e.g. `"__Host-pcs_at"` (chat-auth), `"__Host-rcw_at"` (RCW),
    /// `"__Host-ctw_at"` (CTW).
    pub access_cookie_name: &'static str,
    /// Add-based session-cookie clearance closure. Invoked on
    /// dead-session 401 with a fresh [`CookieJar`]; the returned jar's
    /// add-list becomes the response's Set-Cookie headers. Consumers
    /// typically wrap their `cookies::clear_session_cookies` helper
    /// (which clears AT + RT, and possibly CSRF in future).
    pub on_clear: Arc<dyn Fn(CookieJar) -> CookieJar + Send + Sync>,
    /// Pre-auth path allowlist — requests whose **exact** URI path
    /// matches an entry skip the perimeter entirely (no Bearer
    /// extraction, no `verify_token`, no session injected). The sole
    /// use is anonymous-by-contract transport routes: native gRPC
    /// health probes (`/grpc.health.v1.Health/Check` + `/Watch`),
    /// which Kubernetes issues with no credential. Matching is exact
    /// full-path `==`, never a prefix — a prefix would be a fail-open
    /// over-exemption (a sibling RPC sharing the prefix would silently
    /// bypass auth). The empty slice is the fail-closed default (every
    /// route authenticates); a consumer that exempts nothing passes
    /// `[].into()` explicitly.
    pub path_exempt: Arc<[&'static str]>,
}

impl BearerAuthConfig {
    /// Build a perimeter config from the cookie name, clearance
    /// closure, and pre-auth path exemptions.
    ///
    /// `path_exempt` is **required**, never defaulted: each perimeter
    /// declares its exemption posture explicitly at the construction
    /// site. Pass `[].into()` to exempt nothing (the fail-closed
    /// default), or a consumer-owned `const &[&str]` of exact gRPC
    /// method paths for anonymous-by-contract routes.
    #[must_use]
    pub fn new(
        access_cookie_name: &'static str,
        on_clear: Arc<dyn Fn(CookieJar) -> CookieJar + Send + Sync>,
        path_exempt: Arc<[&'static str]>,
    ) -> Self {
        Self {
            access_cookie_name,
            on_clear,
            path_exempt,
        }
    }
}