jwt_lab/
verify.rs

1use crate::{types::{Jwt, Algorithm}, jwk::{Key, Jwks}, time::{Leeway, validate_claim_times}, Error, Result};
2
3/// Options for JWT verification
4#[derive(Debug, Clone)]
5pub struct VerifyOptions {
6    /// Whether to validate time-based claims (exp, nbf)
7    pub validate_times: bool,
8    /// Leeway in seconds for time-based claim validation
9    pub leeway: Leeway,
10    /// Expected issuer claim value
11    pub expected_iss: Option<String>,
12    /// Expected audience claim value
13    pub expected_aud: Option<String>,
14    /// Expected algorithm (prevents algorithm confusion attacks)
15    pub expected_alg: Option<Algorithm>,
16    /// Current timestamp for time validation (defaults to current time)
17    pub now_ts: Option<i64>,
18}
19
20impl Default for VerifyOptions {
21    fn default() -> Self {
22        Self {
23            validate_times: true,
24            leeway: Leeway { seconds: 0 },
25            expected_iss: None,
26            expected_aud: None,
27            expected_alg: None,
28            now_ts: None,
29        }
30    }
31}
32impl VerifyOptions {
33    /// Set whether to validate time-based claims
34    pub fn validate_times(mut self, v: bool) -> Self { self.validate_times = v; self }
35    /// Set the leeway in seconds for time validation
36    pub fn leeway(mut self, s: i64) -> Self { self.leeway = Leeway { seconds: s }; self }
37    /// Set the expected issuer claim value
38    pub fn expect_iss(mut self, iss: impl Into<String>) -> Self { self.expected_iss = Some(iss.into()); self }
39    /// Set the expected audience claim value
40    pub fn expect_aud(mut self, aud: impl Into<String>) -> Self { self.expected_aud = Some(aud.into()); self }
41    /// Set the expected algorithm (prevents algorithm confusion attacks)
42    pub fn expect_alg(mut self, alg: Algorithm) -> Self { self.expected_alg = Some(alg); self }
43    /// Set the current timestamp for time validation
44    pub fn now_ts(mut self, ts: i64) -> Self { self.now_ts = Some(ts); self }
45}
46
47/// Convert our Algorithm enum to jsonwebtoken's Algorithm enum
48fn to_jalg(a: Algorithm) -> jsonwebtoken::Algorithm {
49    use Algorithm::*;
50    match a {
51        HS256 => jsonwebtoken::Algorithm::HS256,
52        HS384 => jsonwebtoken::Algorithm::HS384,
53        HS512 => jsonwebtoken::Algorithm::HS512,
54        RS256 => jsonwebtoken::Algorithm::RS256,
55        RS384 => jsonwebtoken::Algorithm::RS384,
56        RS512 => jsonwebtoken::Algorithm::RS512,
57        ES256 => jsonwebtoken::Algorithm::ES256,
58        ES384 => jsonwebtoken::Algorithm::ES384,
59        ES512 => jsonwebtoken::Algorithm::ES384,
60        EdDSA => jsonwebtoken::Algorithm::EdDSA,
61    }
62}
63
64/// Verify a JWT with the given key and options
65///
66/// # Arguments
67///
68/// * `jwt` - The JWT to verify
69/// * `key` - The verification key
70/// * `opts` - Verification options
71///
72/// # Returns
73///
74/// Returns `Ok(())` if verification succeeds, or an error if it fails
75///
76/// # Errors
77///
78/// - `Error::Claims` if algorithm mismatch or claim validation fails
79/// - `Error::DisabledAlg` if the algorithm is not enabled via feature flags
80/// - `Error::Key` if the key is invalid
81/// - `Error::Signature` if signature verification fails
82pub fn verify_with_key(jwt: &Jwt, key: &Key, opts: &VerifyOptions) -> Result<()> {
83    use jsonwebtoken::{Validation, DecodingKey, decode};
84
85    if let Some(expected_alg) = opts.expected_alg {
86        if jwt.header.alg != expected_alg {
87            return Err(Error::Claims(format!("algorithm mismatch: expected {:?}, got {:?}", expected_alg, jwt.header.alg)));
88        }
89    }
90
91    match jwt.header.alg {
92        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { #[cfg(not(feature="hs"))] return Err(Error::DisabledAlg("HS")); }
93        Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => { #[cfg(not(feature="rs"))] return Err(Error::DisabledAlg("RS")); }
94        Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => { #[cfg(not(feature="es"))] return Err(Error::DisabledAlg("ES")); }
95        Algorithm::EdDSA => { #[cfg(not(feature="eddsa"))] return Err(Error::DisabledAlg("EdDSA")); }
96    }
97
98    let mut v = Validation::new(to_jalg(jwt.header.alg));
99    v.validate_exp = false;
100    v.insecure_disable_signature_validation();
101    if let Some(ref iss) = opts.expected_iss { v.set_issuer(&[iss.clone()]); }
102    if let Some(ref aud) = opts.expected_aud { v.set_audience(&[aud.clone()]); }
103
104    let dkey = match key {
105        Key::Hs(s) => DecodingKey::from_secret(s),
106        Key::RsaPublicPem(p) => DecodingKey::from_rsa_pem(p).map_err(|e| Error::Key(e.to_string()))?,
107        Key::EcPublicPem(p) => DecodingKey::from_ec_pem(p).map_err(|e| Error::Key(e.to_string()))?,
108        Key::EdPublicPem(p) => DecodingKey::from_ed_pem(p).map_err(|e| Error::Key(e.to_string()))?,
109        _ => return Err(Error::Key("public or shared secret required to verify".into())),
110    };
111
112    let token_str = format!("{}.{}.{}", jwt.raw_header_b64, jwt.raw_payload_b64, jwt.signature_b64);
113    let data = decode::<serde_json::Value>(&token_str, &dkey, &v).map_err(|e| Error::Signature(e.to_string()))?;
114
115    if opts.validate_times {
116        let now = opts.now_ts.unwrap_or_else(|| time::OffsetDateTime::now_utc().unix_timestamp());
117        let map = data.claims.as_object().cloned().unwrap_or_default();
118        validate_claim_times(&map, true, opts.leeway, now)?;
119    }
120    Ok(())
121}
122
123impl Jwt {
124    /// Verify this JWT with the given key and options
125    ///
126    /// # Arguments
127    ///
128    /// * `key` - The verification key
129    /// * `opts` - Verification options
130    ///
131    /// # Returns
132    ///
133    /// Returns `Ok(())` if verification succeeds, or an error if it fails
134    pub fn verify(&self, key: &Key, opts: VerifyOptions) -> Result<()> {
135        verify_with_key(self, key, &opts)
136    }
137    /// Verify this JWT using a JWKS (JSON Web Key Set)
138    ///
139    /// # Arguments
140    ///
141    /// * `jwks` - The JWKS containing the verification keys
142    /// * `opts` - Verification options
143    ///
144    /// # Returns
145    ///
146    /// Returns `Ok(())` if verification succeeds, or an error if it fails
147    ///
148    /// # Errors
149    ///
150    /// - `Error::MissingKid` if the JWT header doesn't contain a `kid` field
151    /// - `Error::KidNotFound` if the `kid` is not found in the JWKS
152    pub fn verify_with_jwks(&self, jwks: &Jwks, opts: VerifyOptions) -> Result<()> {
153        let kid = self.header.kid.clone().ok_or(crate::Error::MissingKid)?;
154        let key = jwks.select_for(&kid, self.header.alg)?;
155        verify_with_key(self, &key, &opts)
156    }
157}