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