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}
14
15#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
19#[serde(rename_all = "lowercase")]
20pub enum TokenType {
21 Access,
22 Refresh,
23}
24
25#[derive(Debug, Serialize, Deserialize, Clone)]
26pub struct TokenClaims {
27 pub sub: String,
28 pub token_uuid: String,
29 #[serde(default)]
30 pub token_type: Option<TokenType>,
31 pub exp: i64,
32 pub iat: i64,
33 pub nbf: i64,
34}
35
36fn decode_pem(b64: &str) -> Result<Vec<u8>, JwtError> {
39 general_purpose::STANDARD
40 .decode(b64)
41 .map_err(|_| JwtError::from(ErrorKind::InvalidKeyFormat))
42}
43
44pub fn generate_jwt_token(
45 user_id: uuid::Uuid,
46 ttl: i64,
47 private_key: &str,
48 token_type: TokenType,
49) -> Result<TokenDetails, JwtError> {
50 generate_jwt_token_with_uuid(user_id, Uuid::new_v4(), ttl, private_key, token_type)
51}
52
53pub fn generate_jwt_token_with_uuid(
54 user_id: uuid::Uuid,
55 token_uuid: uuid::Uuid,
56 ttl: i64,
57 private_key: &str,
58 token_type: TokenType,
59) -> Result<TokenDetails, JwtError> {
60 let decoded_private_key = decode_pem(private_key)?;
61
62 let now = chrono::Utc::now();
63 let mut token_details = TokenDetails {
64 user_id,
65 token_uuid,
66 token_type: Some(token_type),
67 expires_in: Some((now + chrono::Duration::minutes(ttl)).timestamp()),
68 token: None,
69 };
70
71 let claims = TokenClaims {
72 sub: token_details.user_id.to_string(),
73 token_uuid: token_details.token_uuid.to_string(),
74 token_type: Some(token_type),
75 exp: token_details.expires_in.unwrap_or_default(),
76 iat: now.timestamp(),
77 nbf: now.timestamp(),
78 };
79
80 let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256);
81 let token = jsonwebtoken::encode(
82 &header,
83 &claims,
84 &jsonwebtoken::EncodingKey::from_rsa_pem(&decoded_private_key)?,
85 )?;
86 token_details.token = Some(token);
87 Ok(token_details)
88}
89
90pub fn verify_jwt_token(public_key: &str, token: &str) -> Result<TokenDetails, JwtError> {
91 let decoded_public_key = decode_pem(public_key)?;
92
93 let validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::RS256);
94
95 let decoded = jsonwebtoken::decode::<TokenClaims>(
96 token,
97 &jsonwebtoken::DecodingKey::from_rsa_pem(&decoded_public_key)?,
98 &validation,
99 )?;
100
101 let token_details = claims_to_details(&decoded.claims)?;
102 Ok(token_details)
103}
104
105pub fn unverified_user_id(token: &str) -> Result<Uuid, JwtError> {
116 let decoded = jsonwebtoken::dangerous::insecure_decode::<TokenClaims>(token)?;
117 Uuid::parse_str(&decoded.claims.sub).map_err(|_| JwtError::from(ErrorKind::InvalidSubject))
118}
119
120fn claims_to_details(claims: &TokenClaims) -> Result<TokenDetails, JwtError> {
121 let user_id =
122 Uuid::parse_str(&claims.sub).map_err(|_| JwtError::from(ErrorKind::InvalidSubject))?;
123 let token_uuid =
124 Uuid::parse_str(&claims.token_uuid).map_err(|_| JwtError::from(ErrorKind::InvalidToken))?;
125 Ok(TokenDetails {
126 token: None,
127 token_uuid,
128 user_id,
129 token_type: claims.token_type,
130 expires_in: None,
131 })
132}