rustauth_plugins/jwt/
verify.rs1use base64::engine::general_purpose::URL_SAFE_NO_PAD;
2use base64::Engine;
3use josekit::jws::alg::ecdsa::EcdsaJwsAlgorithm::{Es256, Es512};
4use josekit::jws::alg::eddsa::EddsaJwsAlgorithm::Eddsa;
5use josekit::jws::alg::rsassa::RsassaJwsAlgorithm::Rs256;
6use josekit::jws::alg::rsassa_pss::RsassaPssJwsAlgorithm::Ps256;
7use josekit::jwt;
8use rustauth_core::context::AuthContext;
9use rustauth_core::error::RustAuthError;
10use serde_json::Value;
11
12use super::claims::JwtClaims;
13use super::{adapter, JwkAlgorithm, JwtOptions};
14
15pub async fn verify_jwt(
16 context: &AuthContext,
17 token: &str,
18 issuer_override: Option<&str>,
19) -> Result<Option<JwtClaims>, RustAuthError> {
20 verify_jwt_with_options(context, token, &JwtOptions::default(), issuer_override).await
21}
22
23pub async fn verify_jwt_with_options(
24 context: &AuthContext,
25 token: &str,
26 options: &JwtOptions,
27 issuer_override: Option<&str>,
28) -> Result<Option<JwtClaims>, RustAuthError> {
29 let Some(kid) = token_kid(token) else {
30 return Ok(None);
31 };
32 let Some(key) = adapter::get_all_keys(context, options)
33 .await?
34 .into_iter()
35 .find(|key| key.id == kid)
36 else {
37 return Ok(None);
38 };
39 let algorithm = key.alg.unwrap_or_else(|| options.algorithm());
40 let Ok(public) = josekit::jwk::Jwk::from_bytes(&key.public_key) else {
41 return Ok(None);
42 };
43 let decoded = match algorithm {
44 JwkAlgorithm::EdDsa => {
45 let Ok(verifier) = Eddsa.verifier_from_jwk(&public) else {
46 return Ok(None);
47 };
48 jwt::decode_with_verifier(token, &verifier)
49 }
50 JwkAlgorithm::Es256 => {
51 let Ok(verifier) = Es256.verifier_from_jwk(&public) else {
52 return Ok(None);
53 };
54 jwt::decode_with_verifier(token, &verifier)
55 }
56 JwkAlgorithm::Es512 => {
57 let Ok(verifier) = Es512.verifier_from_jwk(&public) else {
58 return Ok(None);
59 };
60 jwt::decode_with_verifier(token, &verifier)
61 }
62 JwkAlgorithm::Rs256 => {
63 let Ok(verifier) = Rs256.verifier_from_jwk(&public) else {
64 return Ok(None);
65 };
66 jwt::decode_with_verifier(token, &verifier)
67 }
68 JwkAlgorithm::Ps256 => {
69 let Ok(verifier) = Ps256.verifier_from_jwk(&public) else {
70 return Ok(None);
71 };
72 jwt::decode_with_verifier(token, &verifier)
73 }
74 };
75 let Ok((payload, _)) = decoded else {
76 return Ok(None);
77 };
78 let claims = payload.claims_set().clone();
79 if !valid_temporal_claims(&claims) || !valid_issuer(&claims, context, options, issuer_override)
80 {
81 return Ok(None);
82 }
83 if claims.get("sub").and_then(Value::as_str).is_none()
84 || !valid_audience(&claims, context, options)
85 {
86 return Ok(None);
87 }
88 Ok(Some(claims))
89}
90
91fn token_kid(token: &str) -> Option<String> {
92 let mut parts = token.split('.');
93 let header = parts.next()?;
94 parts.next()?;
95 parts.next()?;
96 if parts.next().is_some() {
97 return None;
98 }
99 let header = URL_SAFE_NO_PAD.decode(header).ok()?;
100 let header: Value = serde_json::from_slice(&header).ok()?;
101 header.get("kid").and_then(Value::as_str).map(str::to_owned)
102}
103
104fn valid_temporal_claims(claims: &JwtClaims) -> bool {
105 let now = time::OffsetDateTime::now_utc().unix_timestamp();
106 if claims
107 .get("exp")
108 .and_then(Value::as_i64)
109 .is_some_and(|exp| exp <= now)
110 {
111 return false;
112 }
113 if claims
114 .get("nbf")
115 .and_then(Value::as_i64)
116 .is_some_and(|nbf| nbf > now)
117 {
118 return false;
119 }
120 true
121}
122
123fn valid_issuer(
124 claims: &JwtClaims,
125 context: &AuthContext,
126 options: &JwtOptions,
127 issuer_override: Option<&str>,
128) -> bool {
129 let expected = issuer_override
130 .map(str::to_owned)
131 .or_else(|| options.jwt.issuer.clone())
132 .unwrap_or_else(|| context.base_url.clone());
133 claims.get("iss").and_then(Value::as_str) == Some(expected.as_str())
134}
135
136fn valid_audience(claims: &JwtClaims, context: &AuthContext, options: &JwtOptions) -> bool {
137 let expected = options
138 .jwt
139 .audience
140 .clone()
141 .unwrap_or_else(|| vec![context.base_url.clone()]);
142 match claims.get("aud") {
143 Some(Value::String(audience)) => expected.iter().any(|item| item == audience),
144 Some(Value::Array(audiences)) => audiences
145 .iter()
146 .filter_map(Value::as_str)
147 .any(|audience| expected.iter().any(|item| item == audience)),
148 _ => false,
149 }
150}