Skip to main content

pas_external/pas_port/
port.rs

1//! `PasAuthPort` — the network boundary at PAS Authorization Server.
2//!
3//! Production adapter: `AuthClient` (in `crate::oauth`).
4//! Test adapter: `MemoryPasAuth` (in `crate::pas_port::memory`, gated
5//! behind the `test-support` Cargo feature).
6
7use std::future::Future;
8
9use crate::error::Error;
10use crate::oauth::TokenResponse;
11
12/// Minimum surface the SDK refresh flow needs from PAS.
13///
14/// Adapters translate transport-level errors into [`PasFailure`].
15/// Once a `PasFailure` is in hand, all SDK policy code is HTTP-free.
16///
17/// Phase 11.Y removed the `userinfo()` method — the legacy
18/// session-cookie middleware that consumed it was deleted, and the
19/// surviving consumers (`session_liveness::attempt_liveness_refresh`
20/// + `oidc::RelyingParty::refresh`) need only the `/token` round-trip.
21pub trait PasAuthPort: Send + Sync + 'static {
22    /// `POST /oauth/token` with `grant_type=refresh_token` (RFC 6749 §6).
23    fn refresh(
24        &self,
25        refresh_token: &str,
26    ) -> impl Future<Output = Result<TokenResponse, PasFailure>> + Send;
27}
28
29/// Classified PAS-side failure. Single source of truth for the
30/// HTTP-status → cause mapping; only adapters produce these.
31#[non_exhaustive]
32#[derive(Debug, Clone)]
33pub enum PasFailure {
34    /// PAS returned 4xx — credential is dead. Refresh-token expired,
35    /// revoked, or user logged out elsewhere.
36    Rejected { status: u16, detail: String },
37    /// PAS returned 5xx — service is degraded. Retry; do not invalidate.
38    ServerError { status: u16, detail: String },
39    /// HTTP-level transport issue (timeout, TLS, DNS, connect) OR a
40    /// 2xx whose body failed to parse (CDN/proxy garbage).
41    /// Indistinguishable from a true network blip; fail-open paths
42    /// serve cache, fail-closed paths reject.
43    Transport { detail: String },
44}
45
46impl PasFailure {
47    /// Lossy conversion to the legacy [`Error::OAuth`] shape, used by
48    /// inherent `AuthClient` methods that retain the v4 signature
49    /// (currently just `exchange_code`).
50    #[must_use]
51    pub fn into_legacy_error(self, operation: &'static str) -> Error {
52        match self {
53            Self::Rejected { status, detail } | Self::ServerError { status, detail } => {
54                Error::OAuth { operation, status: Some(status), detail }
55            }
56            Self::Transport { detail } => Error::OAuth { operation, status: None, detail },
57        }
58    }
59}