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
//! Tonic request interceptor that attaches `Authorization: Bearer <token>`
//! to every outgoing gRPC call.
//!
//! ## Usage
//!
//! ```ignore
//! use ppoppo_sdk_core::interceptor::{AuthInterceptor, BearerCredential};
//! use ppoppo_sdk_core::token_cache::TokenCache;
//!
//! // Fetch a fresh token from the cache (async), then create the interceptor.
//! let token: String = cache.get().await?;
//! let cred = BearerCredential::new(token);
//! let interceptor = AuthInterceptor::new(cred);
//!
//! // Attach to any tonic service client:
//! let client = MyServiceClient::with_interceptor(channel, interceptor);
//! ```
//!
//! The interceptor is synchronous — tonic's `Interceptor::call` has no async
//! capability. Token refresh is the caller's responsibility (done before each
//! call via `TokenCache::get`).

use tonic::{Request, Status, service::Interceptor};

/// A raw Bearer JWT string. Newtypes it from plain `String` so call sites
/// are explicit about what they're passing around.
#[derive(Clone)]
pub struct BearerCredential(String);

impl BearerCredential {
    pub fn new(token: String) -> Self {
        Self(token)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Debug for BearerCredential {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("BearerCredential([redacted])")
    }
}

/// Tonic [`Interceptor`] that injects `Authorization: Bearer <token>` into
/// every outgoing request's metadata.
///
/// Constructed from a [`BearerCredential`] obtained via `TokenCache::get`.
/// One instance per RPC call — create it fresh each time so the token is
/// always current.
pub struct AuthInterceptor(BearerCredential);

impl AuthInterceptor {
    pub fn new(credential: BearerCredential) -> Self {
        Self(credential)
    }
}

impl Interceptor for AuthInterceptor {
    fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
        let header_value = format!("Bearer {}", self.0.as_str());
        let meta_val = tonic::metadata::MetadataValue::try_from(header_value.as_str())
            .map_err(|_| Status::internal("bearer token contains non-ASCII characters"))?;
        request.metadata_mut().insert("authorization", meta_val);
        Ok(request)
    }
}