Validates a JWT token string and returns the decoded claims. # Errors - [`Validation`] if the token header cannot be decoded, signature verification fails, or claims are invalid (expired, wrong audience/issuer) - [`MissingKeyId`] if the token has no `kid` header - [`JwksFetch`] if the JWKS endpoint cannot be reached - [`UnknownKeyId`] if the `kid` does not match any key in the JWKS [`Validation
(&self, token: &str)
| 163 | /// [`JwksFetch`]: JwtError::JwksFetch |
| 164 | /// [`UnknownKeyId`]: JwtError::UnknownKeyId |
| 165 | pub async fn validate(&self, token: &str) -> Result<JwtClaims, Report<JwtError>> { |
| 166 | let header = decode_header(token).change_context(JwtError::Validation)?; |
| 167 | |
| 168 | if !self.allowed_algorithms.contains(&header.alg) { |
| 169 | return Err(Report::new(JwtError::Validation)) |
| 170 | .attach(format!("algorithm {:?} is not allowed", header.alg)); |
| 171 | } |
| 172 | |
| 173 | let kid = header.kid.ok_or(JwtError::MissingKeyId)?; |
| 174 | |
| 175 | let decoding_key = self.resolve_decoding_key(&kid).await?; |
| 176 | |
| 177 | // jsonwebtoken v10 requires all algorithms in the validation list to share the same key |
| 178 | // family, so we pass only the token's algorithm (already checked above). |
| 179 | let mut validation = Validation::new(header.alg); |
| 180 | validation.set_audience(&[&self.audience]); |
| 181 | validation.set_issuer(&[&self.issuer]); |
| 182 | |
| 183 | let raw = decode::<RawJwtClaims>(token, &decoding_key, &validation) |
| 184 | .change_context(JwtError::Validation)? |
| 185 | .claims; |
| 186 | |
| 187 | let issued_at = OffsetDateTime::from_unix_timestamp( |
| 188 | i64::try_from(raw.iat).change_context(JwtError::Validation)?, |
| 189 | ) |
| 190 | .change_context(JwtError::Validation)?; |
| 191 | |
| 192 | let expires_at = OffsetDateTime::from_unix_timestamp( |
| 193 | i64::try_from(raw.exp).change_context(JwtError::Validation)?, |
| 194 | ) |
| 195 | .change_context(JwtError::Validation)?; |
| 196 | |
| 197 | Ok(JwtClaims { |
| 198 | sub: raw.sub, |
| 199 | email: raw.email, |
| 200 | issued_at, |
| 201 | expires_at, |
| 202 | }) |
| 203 | } |
| 204 | |
| 205 | /// Resolves a [`DecodingKey`] for the given key ID from the cached JWKS. |
| 206 | /// |
no test coverage detected