Skip to main content

authbox_jwt/
jwt.rs

1use super::errors::JwtError;
2use super::models::{AuthTokens, JwtClaims, TokenType};
3use async_trait::async_trait;
4use authbox_core::prelude::{BlacklistableClaims, TokenManager};
5use chrono::{Duration, Utc};
6use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode};
7use uuid::Uuid;
8
9#[derive(Debug, Clone)]
10pub struct DefaultJwtManager {
11    pub secret_key: String,
12}
13
14/// Default JWT implementation
15impl DefaultJwtManager {
16    pub fn new<T: Into<String>>(secret_key: T) -> Self {
17        Self {
18            secret_key: secret_key.into(),
19        }
20    }
21}
22
23/// JwtClaims type must implement BlacklistableClaims trait
24impl BlacklistableClaims for JwtClaims {
25    fn jti(&self) -> &str {
26        &self.jti
27    }
28
29    fn exp(&self) -> i64 {
30        self.exp
31    }
32}
33
34#[async_trait]
35impl TokenManager for DefaultJwtManager {
36    type Token = AuthTokens;
37    type Claims = JwtClaims;
38    type Error = JwtError;
39
40    async fn generate(&self, user_id: &str) -> Result<Self::Token, Self::Error> {
41        let access_exp = (Utc::now() + Duration::minutes(15)).timestamp();
42        let refresh_exp = (Utc::now() + Duration::days(1)).timestamp();
43
44        let access_claims = JwtClaims {
45            sub: user_id.to_string(),
46            exp: access_exp,
47            jti: Uuid::new_v4().to_string(),
48            token_type: TokenType::Access,
49        };
50
51        let refresh_claims = JwtClaims {
52            sub: user_id.to_string(),
53            exp: refresh_exp,
54            jti: Uuid::new_v4().to_string(),
55            token_type: TokenType::Refresh,
56        };
57
58        let header = Header::new(Algorithm::HS256);
59
60        let access_token = encode(
61            &header,
62            &access_claims,
63            &EncodingKey::from_secret(self.secret_key.as_bytes()),
64        )
65        .map_err(JwtError::Encode)?;
66
67        let refresh_token = encode(
68            &header,
69            &refresh_claims,
70            &EncodingKey::from_secret(self.secret_key.as_bytes()),
71        )
72        .map_err(JwtError::Encode)?;
73
74        Ok(AuthTokens {
75            access_token,
76            refresh_token,
77            access_expires_at: access_exp,
78            token_type: "Bearer".to_string(),
79        })
80    }
81
82    async fn verify(&self, token: &str) -> Result<Self::Claims, Self::Error> {
83        let mut validation = Validation::new(Algorithm::HS256);
84        validation.validate_exp = true;
85        validation.validate_nbf = true;
86
87        decode::<JwtClaims>(
88            token,
89            &DecodingKey::from_secret(self.secret_key.as_bytes()),
90            &validation,
91        )
92        .map(|data| data.claims)
93        .map_err(JwtError::Decode)
94    }
95
96    async fn refresh(&self, refresh_token: &str) -> Result<Self::Token, Self::Error> {
97        let claims = self.verify(refresh_token).await?;
98
99        match claims.token_type {
100            TokenType::Refresh => {}
101            _ => return Err(JwtError::InvalidTokenType),
102        }
103
104        let new_access_exp = (Utc::now() + Duration::minutes(15)).timestamp();
105        let new_refresh_exp = (Utc::now() + Duration::days(7)).timestamp();
106
107        let new_access_claims = JwtClaims {
108            sub: claims.sub.clone(),
109            exp: new_access_exp,
110            jti: Uuid::new_v4().to_string(),
111            token_type: TokenType::Access,
112        };
113
114        let new_refresh_claims = JwtClaims {
115            sub: claims.sub,
116            exp: new_refresh_exp,
117            jti: Uuid::new_v4().to_string(),
118            token_type: TokenType::Refresh,
119        };
120
121        let header = Header::new(Algorithm::HS256);
122
123        let access_token = encode(
124            &header,
125            &new_access_claims,
126            &EncodingKey::from_secret(self.secret_key.as_bytes()),
127        )
128        .map_err(JwtError::Encode)?;
129
130        let refresh_token = encode(
131            &header,
132            &new_refresh_claims,
133            &EncodingKey::from_secret(self.secret_key.as_bytes()),
134        )
135        .map_err(JwtError::Encode)?;
136
137        Ok(AuthTokens {
138            access_token,
139            refresh_token,
140            access_expires_at: new_access_exp,
141            token_type: "Bearer".to_string(),
142        })
143    }
144}