axum_jwt_auth/local.rs
1use async_trait::async_trait;
2use derive_builder::Builder;
3use jsonwebtoken::{DecodingKey, TokenData, Validation};
4use serde::de::DeserializeOwned;
5
6use crate::{Error, JwtDecoder};
7
8/// JWT decoder that validates tokens using locally stored keys.
9///
10/// Supports multiple decoding keys and tries them sequentially until one succeeds.
11/// Ideal for scenarios with key rotation or multiple valid signing keys.
12///
13/// # Example
14///
15/// ```ignore
16/// use axum_jwt_auth::LocalDecoder;
17/// use jsonwebtoken::{DecodingKey, Algorithm, Validation};
18///
19/// let keys = vec![DecodingKey::from_secret(b"secret")];
20/// let mut validation = Validation::new(Algorithm::HS256);
21/// validation.set_audience(&["my-app"]);
22///
23/// let decoder = LocalDecoder::builder()
24/// .keys(keys)
25/// .validation(validation)
26/// .build()
27/// .unwrap();
28/// ```
29#[derive(Clone, Builder)]
30pub struct LocalDecoder {
31 keys: Vec<DecodingKey>,
32 validation: Validation,
33}
34
35impl LocalDecoder {
36 /// Creates a new `LocalDecoder` with the specified keys and validation settings.
37 ///
38 /// # Errors
39 ///
40 /// Returns `Error::Configuration` if:
41 /// - No decoding keys are provided
42 /// - No validation algorithms are configured
43 /// - No audience is specified in validation
44 pub fn new(keys: Vec<DecodingKey>, validation: Validation) -> Result<Self, Error> {
45 if keys.is_empty() {
46 return Err(Error::Configuration("No decoding keys provided".into()));
47 }
48
49 if validation.algorithms.is_empty() {
50 return Err(Error::Configuration(
51 "Validation algorithm is required".into(),
52 ));
53 }
54
55 if validation.aud.is_none() {
56 return Err(Error::Configuration(
57 "Validation audience is required".into(),
58 ));
59 }
60
61 Ok(Self { keys, validation })
62 }
63
64 /// Creates a new `LocalDecoderBuilder` for configuring a decoder.
65 pub fn builder() -> LocalDecoderBuilder {
66 LocalDecoderBuilder::default()
67 }
68}
69
70#[async_trait]
71impl<T> JwtDecoder<T> for LocalDecoder
72where
73 T: for<'de> DeserializeOwned,
74{
75 async fn decode(&self, token: &str) -> Result<TokenData<T>, Error> {
76 // Try to decode the token with each key in the cache
77 // If none of them work, return the error from the last one
78 let mut last_error: Option<Error> = None;
79 for key in self.keys.iter() {
80 match jsonwebtoken::decode::<T>(token, key, &self.validation) {
81 Ok(token_data) => return Ok(token_data),
82 Err(e) => {
83 tracing::error!("Error decoding token: {}", e);
84 last_error = Some(Error::Jwt(e));
85 }
86 }
87 }
88
89 Err(last_error.unwrap_or_else(|| Error::Configuration("No keys available".into())))
90 }
91}