ppoppo-sdk-core 0.2.0

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
//! In-memory [`BearerVerifier`] adapter for tests + boundary fixtures.
//!
//! Gated behind `cfg(any(test, feature = "test-support"))` so production
//! builds never link the test machinery.
//!
//! Two ways to fail a verification:
//!
//! - **Lookup miss**: the token string isn't in the `sessions` map.
//!   Surfaces as [`VerifyError::SignatureInvalid`] (the boundary
//!   semantic of "this token doesn't verify against our keyset" maps
//!   identically to "this token is unknown to the test fixture").
//! - **Default failure**: any token returns the configured error.
//!   Used to simulate keyset outages, expired tokens, etc. without
//!   pre-registering every variant.

use std::collections::HashMap;

use async_trait::async_trait;

use super::{BearerVerifier, VerifiedClaims, VerifyConfig, VerifyError};

/// In-memory verifier — pre-registered token → `VerifiedClaims` map.
#[derive(Default)]
pub struct MemoryBearerVerifier {
    sessions: HashMap<String, VerifiedClaims>,
    default_failure: Option<VerifyError>,
    /// Optional [`VerifyConfig`] stored for symmetry with
    /// [`super::JwtVerifier`] construction. The in-memory verifier
    /// doesn't consult them by default — the test fixture controls
    /// success/failure via the `sessions` map and `default_failure`.
    /// Callers that want to simulate iss/aud mismatches do so by
    /// inserting an `IssuerInvalid` / `AudienceInvalid`
    /// `default_failure`.
    #[allow(dead_code)]
    expectations: Option<VerifyConfig>,
}

impl MemoryBearerVerifier {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Attach [`VerifyConfig`] for symmetry with [`super::JwtVerifier`]
    /// construction. The in-memory adapter does not currently consult
    /// it; it's a hook point for future "simulate iss/aud check"
    /// semantics.
    #[must_use]
    pub fn with_expectations(mut self, expectations: VerifyConfig) -> Self {
        self.expectations = Some(expectations);
        self
    }

    /// Register a successful verification: when `token` is presented,
    /// return `claims`.
    pub fn insert(&mut self, token: impl Into<String>, claims: VerifiedClaims) -> &mut Self {
        self.sessions.insert(token.into(), claims);
        self
    }

    /// Configure a global default failure — every token (including
    /// those in the `sessions` map) returns this error. Useful for
    /// simulating a keyset outage in consumer integration tests.
    pub fn fail_with(&mut self, err: VerifyError) -> &mut Self {
        self.default_failure = Some(err);
        self
    }
}

#[async_trait]
impl BearerVerifier for MemoryBearerVerifier {
    async fn verify(&self, bearer_token: &str) -> Result<VerifiedClaims, VerifyError> {
        if let Some(err) = self.default_failure.clone() {
            return Err(err);
        }
        self.sessions
            .get(bearer_token)
            .cloned()
            .ok_or(VerifyError::SignatureInvalid)
    }
}