Skip to main content

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}