Skip to main content

ppoppo_token/access_token/
verify_config.rs

1//! Per-request verification configuration for the RFC 9068 access-token
2//! profile.
3//!
4//! ── Composition ────────────────────────────────────────────────────────
5//!
6//! JOSE-shared fields (`issuer`, `audience`, `expected_typ`,
7//! `max_token_size`, `algorithms`) live in `engine::SharedVerifyConfig`
8//! and are reached via `self.shared`. Access-token-specific axes
9//! (replay/session/epoch revocation ports) stay on this struct. The
10//! engine submodules `check_algorithm` / `check_header` / `raw` read
11//! only from `&SharedVerifyConfig`; `check_claims` / `check_domain` /
12//! revocation checks read from this struct (and reach the shared
13//! fields via `cfg.shared.*`).
14//!
15//! ── Phase 5 — orthogonal port slots ────────────────────────────────────
16//!
17//! Three optional ports model the orthogonal revocation axes (M35-M38 +
18//! sv). Each is `Option<Arc<dyn ...>>` so callers wire only what their
19//! deployment substrate supports — `None` short-circuits the gate
20//! (legacy admit / sibling-test config / migration phases).
21
22use std::sync::Arc;
23
24use super::epoch_revocation::EpochRevocation;
25use super::replay_defense::ReplayDefense;
26use super::session_revocation::SessionRevocation;
27use crate::algorithm::Algorithm;
28use crate::engine::shared_config::SharedVerifyConfig;
29
30#[derive(Debug, Clone)]
31#[allow(dead_code)] // ports consumed across Phase 5+
32pub struct VerifyConfig {
33    pub(crate) shared: SharedVerifyConfig,
34
35    // ── Phase 5 revocation port slots ──────────────────────────────────
36    /// M35 jti replay defense (Phase 5 commit 5.1).
37    pub(crate) replay: Option<Arc<dyn ReplayDefense>>,
38    /// M36 session-row liveness (Phase 5 commit 5.2).
39    pub(crate) session: Option<Arc<dyn SessionRevocation>>,
40    /// sv-port per-account epoch (Phase 5 commits 5.5-5.7).
41    pub(crate) epoch: Option<Arc<dyn EpochRevocation>>,
42}
43
44impl VerifyConfig {
45    /// Canonical access-token config: `at+jwt` typ, sealed-vocabulary
46    /// algorithm whitelist (`Algorithm::ALL` — every alg PAS recognises;
47    /// EdDSA-only today), 8 KB token size cap (M34). All revocation port
48    /// slots default to `None`; callers opt in via the builders.
49    pub fn access_token(issuer: impl Into<String>, audience: impl Into<String>) -> Self {
50        Self {
51            shared: SharedVerifyConfig::new(
52                issuer,
53                audience,
54                "at+jwt",
55                8 * 1024,
56                Algorithm::ALL.to_vec(),
57            ),
58            replay: None,
59            session: None,
60            epoch: None,
61        }
62    }
63
64    /// Override the algorithm whitelist. Test-only escape hatch — production
65    /// callers MUST go through `access_token` so the EdDSA pin is the default.
66    #[must_use]
67    pub fn with_algorithms(mut self, algorithms: Vec<Algorithm>) -> Self {
68        self.shared.algorithms = algorithms;
69        self
70    }
71
72    /// Wire the M35 jti replay defense port.
73    #[must_use]
74    pub fn with_replay_defense(mut self, port: Arc<dyn ReplayDefense>) -> Self {
75        self.replay = Some(port);
76        self
77    }
78
79    /// Wire the M36 session-row liveness port.
80    #[must_use]
81    pub fn with_session_revocation(mut self, port: Arc<dyn SessionRevocation>) -> Self {
82        self.session = Some(port);
83        self
84    }
85
86    /// Wire the sv-port per-account epoch revocation.
87    #[must_use]
88    pub fn with_epoch_revocation(mut self, port: Arc<dyn EpochRevocation>) -> Self {
89        self.epoch = Some(port);
90        self
91    }
92}