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
14impl DefaultJwtManager {
16 pub fn new<T: Into<String>>(secret_key: T) -> Self {
17 Self {
18 secret_key: secret_key.into(),
19 }
20 }
21}
22
23impl 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}