Skip to main content

ppoppo_token/id_token/
nonce.rs

1//! OIDC nonce — opaque RP-minted string, gated by M66.
2//!
3//! OIDC Core 1.0 §3.1.2.1 + §15.5.2: the RP generates an unguessable
4//! per-session value, sends it in the Authentication Request, stores a
5//! copy bound to the user's browser session, and compares the id_token's
6//! `nonce` claim against the stored copy on token receipt. The engine's
7//! M66 gate is the *comparison*; generation/storage live RP-side.
8//!
9//! ── Type discipline ─────────────────────────────────────────────────────
10//!
11//! `Nonce` is a newtype over `String` with a non-empty invariant — empty
12//! nonces would short-circuit the M66 check trivially (any payload
13//! missing `nonce` would equal an empty expected value). Higher entropy
14//! requirements (length, character set) are RP-side policy and not
15//! enforced here: the engine can't tell whether `"abc123"` is a hash of a
16//! 256-bit random or a guess; the RP that minted it knows.
17//!
18//! Comparison uses plain `==` — the nonce is a public correlator, not a
19//! cryptographic secret. The RP holds one copy and the wire carries the
20//! other; both halves are observable to anyone who can read the auth
21//! request and the id_token. Constant-time comparison would be cargo-
22//! culted security for a value with no secrecy contract.
23
24use crate::id_token::AuthError;
25
26/// Opaque nonce value. Construction validates non-emptiness; the inner
27/// string is private so callers cannot bypass the invariant by minting
28/// `Nonce(String::new())` directly.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Nonce(String);
31
32impl Nonce {
33    /// Construct from an RP-stored nonce string. Errors on empty input.
34    pub fn new(value: impl Into<String>) -> Result<Self, AuthError> {
35        let v = value.into();
36        if v.is_empty() {
37            return Err(AuthError::NonceConfigEmpty);
38        }
39        Ok(Self(v))
40    }
41
42    /// Borrow the inner string for comparison against the token's
43    /// `nonce` claim. `pub(crate)` — only the engine's `check_nonce`
44    /// submodule consumes the raw bytes.
45    pub(crate) fn as_str(&self) -> &str {
46        &self.0
47    }
48}