axum_jwt_auth/
local.rs

1use jsonwebtoken::{DecodingKey, TokenData, Validation};
2use serde::de::DeserializeOwned;
3
4use crate::{Error, JwtDecoder};
5
6/// JWT decoder that validates tokens using locally stored keys.
7///
8/// Supports multiple decoding keys and tries them sequentially until one succeeds.
9/// Ideal for scenarios with key rotation or multiple valid signing keys.
10///
11/// # Example
12///
13/// ```ignore
14/// use axum_jwt_auth::LocalDecoder;
15/// use jsonwebtoken::{DecodingKey, Algorithm, Validation};
16///
17/// let keys = vec![DecodingKey::from_secret(b"secret")];
18/// let mut validation = Validation::new(Algorithm::HS256);
19/// validation.set_audience(&["my-app"]);
20///
21/// let decoder = LocalDecoder::builder()
22///     .keys(keys)
23///     .validation(validation)
24///     .build()
25///     .unwrap();
26/// ```
27#[derive(Clone)]
28pub struct LocalDecoder {
29    keys: Vec<DecodingKey>,
30    validation: Validation,
31}
32
33impl LocalDecoder {
34    /// Creates a new `LocalDecoder` with the specified keys and validation settings.
35    ///
36    /// # Errors
37    ///
38    /// Returns `Error::Configuration` if:
39    /// - No decoding keys are provided
40    /// - No validation algorithms are configured
41    /// - No audience is specified in validation
42    pub fn new(keys: Vec<DecodingKey>, validation: Validation) -> Result<Self, Error> {
43        if keys.is_empty() {
44            return Err(Error::Configuration("No decoding keys provided".into()));
45        }
46
47        if validation.algorithms.is_empty() {
48            return Err(Error::Configuration(
49                "Validation algorithm is required".into(),
50            ));
51        }
52
53        if validation.aud.is_none() {
54            return Err(Error::Configuration(
55                "Validation audience is required".into(),
56            ));
57        }
58
59        Ok(Self { keys, validation })
60    }
61
62    /// Creates a new `LocalDecoderBuilder` for configuring a decoder.
63    pub fn builder() -> LocalDecoderBuilder {
64        LocalDecoderBuilder {
65            keys: None,
66            validation: None,
67        }
68    }
69}
70
71/// Builder for `LocalDecoder`.
72pub struct LocalDecoderBuilder {
73    keys: Option<Vec<DecodingKey>>,
74    validation: Option<Validation>,
75}
76
77impl LocalDecoderBuilder {
78    /// Sets the decoding keys.
79    pub fn keys(mut self, keys: Vec<DecodingKey>) -> Self {
80        self.keys = Some(keys);
81        self
82    }
83
84    /// Sets the validation settings.
85    pub fn validation(mut self, validation: Validation) -> Self {
86        self.validation = Some(validation);
87        self
88    }
89
90    /// Builds the `LocalDecoder`.
91    ///
92    /// # Errors
93    ///
94    /// Returns `Error::Configuration` if required fields are missing or invalid.
95    pub fn build(self) -> Result<LocalDecoder, Error> {
96        let keys = self
97            .keys
98            .ok_or_else(|| Error::Configuration("keys are required".into()))?;
99        let validation = self
100            .validation
101            .ok_or_else(|| Error::Configuration("validation is required".into()))?;
102
103        LocalDecoder::new(keys, validation)
104    }
105}
106
107impl<T> JwtDecoder<T> for LocalDecoder
108where
109    T: for<'de> DeserializeOwned,
110{
111    fn decode<'a>(
112        &'a self,
113        token: &'a str,
114    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<TokenData<T>, Error>> + Send + 'a>>
115    {
116        Box::pin(async move {
117            // Try to decode the token with each key in the cache
118            // If none of them work, return the error from the last one
119            let mut last_error: Option<Error> = None;
120            for key in self.keys.iter() {
121                match jsonwebtoken::decode::<T>(token, key, &self.validation) {
122                    Ok(token_data) => return Ok(token_data),
123                    Err(e) => {
124                        tracing::error!("Error decoding token: {}", e);
125                        last_error = Some(Error::Jwt(e));
126                    }
127                }
128            }
129
130            Err(last_error.unwrap_or_else(|| Error::Configuration("No keys available".into())))
131        })
132    }
133}