tako_rs_plugins/middleware/jwt_auth/verifier.rs
1//! JWT verification contract and constraint configuration.
2
3use std::fmt;
4
5/// Trait for verifying JWT tokens.
6pub trait JwtVerifier: Send + Sync + Clone + 'static {
7 /// Decoded claims inserted into request extensions.
8 type Claims: Send + Sync + Clone + 'static;
9 /// Verification error.
10 type Error: fmt::Display;
11
12 /// Verifies a raw JWT token string.
13 fn verify(&self, token: &str) -> Result<Self::Claims, Self::Error>;
14
15 /// Validate `iss` / `aud` / `leeway` constraints against the decoded claims.
16 ///
17 /// The default implementation **fails closed** when any non-default
18 /// constraint is configured — concrete verifiers MUST override this if they
19 /// want to silently accept (because they already enforce constraints
20 /// internally) or to apply their own logic. Failing closed prevents the
21 /// previous v1.x behavior where custom verifiers silently dropped the
22 /// `VerifyConstraints` configured on `JwtAuth`, leaving iss/aud/leeway
23 /// unenforced.
24 fn validate_constraints(
25 &self,
26 _claims: &Self::Claims,
27 constraints: &VerifyConstraints,
28 ) -> Result<(), ConstraintsNotSupported> {
29 if constraints.issuer.is_some()
30 || constraints.audience.is_some()
31 || constraints.leeway_secs != 0
32 {
33 Err(ConstraintsNotSupported {
34 reason: "this JwtVerifier does not override `validate_constraints`; \
35 configure constraints on the verifier itself or implement \
36 `validate_constraints` on your custom verifier",
37 })
38 } else {
39 Ok(())
40 }
41 }
42}
43
44/// Reported by [`JwtVerifier::validate_constraints`] when the verifier cannot
45/// (or won't) enforce the requested `VerifyConstraints`. The middleware
46/// surfaces this as 401 Unauthorized — fail-closed by design.
47#[derive(Debug, Clone)]
48pub struct ConstraintsNotSupported {
49 /// Human-readable diagnostic surfaced in the 401 response body.
50 pub reason: &'static str,
51}
52
53impl fmt::Display for ConstraintsNotSupported {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "constraints not enforceable: {}", self.reason)
56 }
57}
58
59/// Optional global verification constraints applied on top of the verifier.
60#[derive(Default, Clone)]
61pub struct VerifyConstraints {
62 /// Required issuer (`iss` claim).
63 pub issuer: Option<String>,
64 /// Required audience (`aud` claim).
65 pub audience: Option<String>,
66 /// Allowed clock skew in seconds.
67 pub leeway_secs: u64,
68}