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}