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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! M36 — session-row liveness check (RFC_2026-05-04_jwt-full-adoption Phase 5).
//!
//! Per STANDARDS_JWT_DETAILS_MITIGATION §E: "Even with valid signature,
//! non-expired, non-replayed, verifier checks `user_sessions` row for
//! `(sub, sid)`. Row deletion = revocation. This makes the system
//! **stateful by design** — the OVERVIEW §6 note 'stateless 환상 폐기'
//! lives here."
//!
//! ── Short-circuit conditions ────────────────────────────────────────────
//!
//! Two `None`-paths short-circuit (admit without consulting substrate):
//!
//! 1. `cfg.session = None` — port not wired (legacy / sibling-test
//! config). Same opt-in pattern as `check_replay`.
//! 2. `claims.sid = None` — token is not session-bound. AI-agent /
//! machine flows mint without `sid`; their authority is bounded by
//! the access-token TTL (1h) + sv epoch revocation, not session-row
//! revocation. Pre-Phase-5 tokens (no `sid` yet) also fall here —
//! gradual rollout (R6 admit) until issuance starts emitting `sid`.
//!
//! ── Why this is distinct from `check_epoch` (sv-port) ──────────────────
//!
//! M36 kicks ONE device while siblings stay alive (LogoutSession
//! primitive — STANDARDS_AUTH_INVALIDATION §2.1). sv-port's
//! `EpochRevocation` is account-wide (LogoutAll / break-glass — kicks
//! every prior token for a subject). The two axes are intentionally
//! orthogonal: deleting a session row requires knowing *which* row,
//! while sv bump is a single per-account counter.
//!
//! ── Fail-closed mapping ─────────────────────────────────────────────────
//!
//! `is_active(...) = Ok(true)` → admit (continue).
//! `is_active(...) = Ok(false)` → `AuthError::SessionRevoked` (security signal).
//! `is_active(...) = Err(_)` → `AuthError::SessionLookupUnavailable` (ops signal).
use crateSessionRevocationError;
use crate;
pub async