1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//! M71 — id_token `acr` (Authentication Context Class Reference) gate
//! (OIDC Core 1.0 §3.1.3.7, §5.5.1.1).
//!
//! When the RP requested specific `acr_values` in its auth request,
//! the returned id_token's `acr` claim MUST be one of those values.
//! This is the step-up authentication mechanism: an RP that requires
//! MFA for sensitive operations declares e.g. `acr_values=urn:mace:incommon:iap:silver`
//! and the engine refuses tokens issued at a weaker level.
//!
//! ── Opt-in semantics ────────────────────────────────────────────────────
//!
//! Engine inspects `acr` only when `cfg.acr_values` is `Some(allowlist)`.
//! RPs that did not request a specific level leave it unset and the
//! engine returns `Ok(())` without touching the claim. Symmetric to
//! M70: step-up is conditional on the verifier asking for it.
//!
//! ── Case sensitivity ────────────────────────────────────────────────────
//!
//! `==` comparison only. OIDC Core §5.5.1.1 specifies `acr` as "a string"
//! whose values are by convention case-sensitive URNs (e.g.
//! `urn:mace:incommon:iap:silver`, `urn:mace:incommon:iap:bronze`,
//! `phr`, `phrh`). Case-folding via `eq_ignore_ascii_case` would
//! silently admit tokens whose acr was intended as a *weaker* level —
//! a substitution-style step-up bypass. No `eq_ignore_ascii_case` here;
//! that is enforced by the negative test
//! `tests/id_token_negative.rs::acr_not_in_values_returns_acr_not_allowed`
//! plus the grep-based pre-flight check.
//!
//! ── Strictness when opted in ────────────────────────────────────────────
//!
//! Once `acr_values` is set, missing `acr` is a hard refusal
//! (`AcrMissing`). The §3.1.3.7 wording is "the Client SHOULD check
//! the `acr` Claim value" but the engine elevates SHOULD → MUST per
//! Phase 7 strict-by-default — same philosophy as M69 azp single-aud.
use crate;
pub