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::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
7use uuid::Uuid;
8
9pub struct DefaultJwtManager {
10    pub secret_key: String,
11}
12
13/// Default JWT implementation
14impl DefaultJwtManager {
15    pub fn new<T: Into<String>>(secret_key: T) -> Self {
16        Self {
17            secret_key: secret_key.into(),
18        }
19    }
20}
21
22/// JwtClaims type must implement BlacklistableClaims trait
23/// For token revocation
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
43        let refresh_exp = (Utc::now() + Duration::days(7)).timestamp();
44
45        let access_claims = JwtClaims {
46            sub: user_id.to_string(),
47            exp: access_exp,
48            jti: Uuid::new_v4().to_string(),
49            token_type: TokenType::Access,
50        };
51
52        let refresh_claims = JwtClaims {
53            sub: user_id.to_string(),
54            exp: refresh_exp,
55            jti: Uuid::new_v4().to_string(),
56            token_type: TokenType::Refresh,
57        };
58
59        let access_token = encode(
60            &Header::default(),
61            &access_claims,
62            &EncodingKey::from_secret(self.secret_key.as_bytes()),
63        )
64        .map_err(JwtError::Encode)?;
65
66        let refresh_token = encode(
67            &Header::default(),
68            &refresh_claims,
69            &EncodingKey::from_secret(self.secret_key.as_bytes()),
70        )
71        .map_err(JwtError::Encode)?;
72
73        Ok(AuthTokens {
74            access_token,
75            refresh_token,
76            expires_in: access_exp as usize,
77            token_type: "Bearer".to_string(),
78        })
79    }
80
81    async fn verify(&self, token: &str) -> Result<Self::Claims, Self::Error> {
82        decode::<JwtClaims>(
83            token,
84            &DecodingKey::from_secret(self.secret_key.as_bytes()),
85            &Validation::default(),
86        )
87        .map(|data| data.claims)
88        .map_err(JwtError::Decode)
89    }
90
91    async fn refresh(&self, refresh_token: &str) -> Result<Self::Token, Self::Error> {
92        let claims = self.verify(refresh_token).await?;
93
94        match claims.token_type {
95            TokenType::Refresh => {}
96            _ => return Err(JwtError::InvalidTokenType),
97        }
98
99        let new_access_exp = (Utc::now() + Duration::minutes(15)).timestamp();
100
101        let new_refresh_exp = (Utc::now() + Duration::days(7)).timestamp();
102
103        let new_access_claims = JwtClaims {
104            sub: claims.sub.clone(),
105            exp: new_access_exp,
106            jti: Uuid::new_v4().to_string(),
107            token_type: TokenType::Access,
108        };
109
110        let new_refresh_claims = JwtClaims {
111            sub: claims.sub,
112            exp: new_refresh_exp,
113            jti: Uuid::new_v4().to_string(),
114            token_type: TokenType::Refresh,
115        };
116
117        let access_token = encode(
118            &Header::default(),
119            &new_access_claims,
120            &EncodingKey::from_secret(self.secret_key.as_bytes()),
121        )
122        .map_err(JwtError::Encode)?;
123
124        let refresh_token = encode(
125            &Header::default(),
126            &new_refresh_claims,
127            &EncodingKey::from_secret(self.secret_key.as_bytes()),
128        )
129        .map_err(JwtError::Encode)?;
130
131        Ok(AuthTokens {
132            access_token,
133            refresh_token,
134            expires_in: new_access_exp as usize,
135            token_type: "Bearer".to_string(),
136        })
137    }
138}