1use base64::{Engine as _, engine::general_purpose};
2use jsonwebtoken::errors::{Error as JwtError, ErrorKind};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Serialize, Deserialize)]
7pub struct TokenDetails {
8 pub token: Option<String>,
9 pub token_uuid: uuid::Uuid,
10 pub user_id: uuid::Uuid,
11 pub token_type: Option<TokenType>,
12 pub expires_in: Option<i64>,
13 pub generation: i32,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
22#[serde(rename_all = "lowercase")]
23pub enum TokenType {
24 Access,
25 Refresh,
26}
27
28#[derive(Debug, Serialize, Deserialize, Clone)]
29pub struct TokenClaims {
30 pub sub: String,
31 pub token_uuid: String,
32 pub generation: i32,
35 #[serde(default)]
36 pub token_type: Option<TokenType>,
37 pub exp: i64,
38 pub iat: i64,
39 pub nbf: i64,
40}
41
42fn decode_pem(b64: &str) -> Result<Vec<u8>, JwtError> {
45 general_purpose::STANDARD
46 .decode(b64)
47 .map_err(|_| JwtError::from(ErrorKind::InvalidKeyFormat))
48}
49
50pub fn generate_jwt_token(
51 user_id: uuid::Uuid,
52 ttl: i64,
53 private_key: &str,
54 token_type: TokenType,
55 generation: i32,
56) -> Result<TokenDetails, JwtError> {
57 generate_jwt_token_with_uuid(
58 user_id,
59 Uuid::new_v4(),
60 ttl,
61 private_key,
62 token_type,
63 generation,
64 )
65}
66
67pub fn generate_jwt_token_with_uuid(
68 user_id: uuid::Uuid,
69 token_uuid: uuid::Uuid,
70 ttl: i64,
71 private_key: &str,
72 token_type: TokenType,
73 generation: i32,
74) -> Result<TokenDetails, JwtError> {
75 let decoded_private_key = decode_pem(private_key)?;
76
77 let now = chrono::Utc::now();
78 let mut token_details = TokenDetails {
79 user_id,
80 token_uuid,
81 token_type: Some(token_type),
82 expires_in: Some((now + chrono::Duration::minutes(ttl)).timestamp()),
83 token: None,
84 generation,
85 };
86
87 let claims = TokenClaims {
88 sub: token_details.user_id.to_string(),
89 token_uuid: token_details.token_uuid.to_string(),
90 generation,
91 token_type: Some(token_type),
92 exp: token_details.expires_in.unwrap_or_default(),
93 iat: now.timestamp(),
94 nbf: now.timestamp(),
95 };
96
97 let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256);
98 let token = jsonwebtoken::encode(
99 &header,
100 &claims,
101 &jsonwebtoken::EncodingKey::from_rsa_pem(&decoded_private_key)?,
102 )?;
103 token_details.token = Some(token);
104 Ok(token_details)
105}
106
107pub fn verify_jwt_token(public_key: &str, token: &str) -> Result<TokenDetails, JwtError> {
108 let decoded_public_key = decode_pem(public_key)?;
109
110 let validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::RS256);
111
112 let decoded = jsonwebtoken::decode::<TokenClaims>(
113 token,
114 &jsonwebtoken::DecodingKey::from_rsa_pem(&decoded_public_key)?,
115 &validation,
116 )?;
117
118 let token_details = claims_to_details(&decoded.claims)?;
119 Ok(token_details)
120}
121
122pub fn unverified_user_id(token: &str) -> Result<Uuid, JwtError> {
133 let decoded = jsonwebtoken::dangerous::insecure_decode::<TokenClaims>(token)?;
134 Uuid::parse_str(&decoded.claims.sub).map_err(|_| JwtError::from(ErrorKind::InvalidSubject))
135}
136
137fn claims_to_details(claims: &TokenClaims) -> Result<TokenDetails, JwtError> {
138 let user_id =
139 Uuid::parse_str(&claims.sub).map_err(|_| JwtError::from(ErrorKind::InvalidSubject))?;
140 let token_uuid =
141 Uuid::parse_str(&claims.token_uuid).map_err(|_| JwtError::from(ErrorKind::InvalidToken))?;
142 Ok(TokenDetails {
143 token: None,
144 token_uuid,
145 user_id,
146 token_type: claims.token_type,
147 expires_in: Some(claims.exp),
150 generation: claims.generation,
151 })
152}