1use std::error::Error;
2use serde::{Serialize, Deserialize};
3use sha2::{digest::generic_array::GenericArray, Digest, Sha256};
4use base64::Engine;
5use aes_gcm::{aead::{Aead, OsRng}, AeadCore, Aes256Gcm, KeyInit, Nonce};
6use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
7use chrono::{Utc, Duration};
8use bcrypt::{hash as crypt_hash, DEFAULT_COST};
9
10use crate::auth::claims::Claims;
11
12fn derive_key_from_string(key_str: &str) -> [u8; 32] {
13 let hasher = Sha256::new_with_prefix(key_str.as_bytes());
14 hasher.finalize().into()
15}
16
17#[derive(Serialize, Deserialize)]
18pub struct AuthService {
19 secret_key: String,
20 encryption_key: String,
21}
22impl AuthService {
23
24 pub fn new(secret_key: String, encryption_key: String) -> Self {
25 Self {
26 secret_key,
27 encryption_key,
28 }
29 }
30
31 pub fn hash(input: &str) -> Result<String, Box<dyn Error>> {
32 let mut hasher = Sha256::new_with_prefix(input.as_bytes());
33 hasher.update(input.as_bytes());
34 let result = hasher.finalize();
35 Ok(hex::encode(result))
36 }
37
38 pub fn hash_password(input: &str) -> Result<String, bcrypt::BcryptError> {
39 crypt_hash(input, DEFAULT_COST)
40 }
41
42 pub fn verify_password(password: &str, hash: &str) -> Result<bool, bcrypt::BcryptError> {
43 bcrypt::verify(password, hash)
44 }
45
46 pub fn generate_token<T: Serialize>(&self, email: String, data: T, minutes: i64) -> String {
47
48 let expiration = Utc::now()
49 .checked_add_signed(Duration::minutes(minutes))
50 .expect("valid timestamp")
51 .timestamp() as usize;
52
53 let claims = Claims {
54 sub: email,
55 exp: expiration,
56 iat: Utc::now().timestamp() as usize,
57 data: data,
58 };
59
60 encode(
61 &Header::default(),
62 &claims,
63 &EncodingKey::from_secret(self.secret_key.as_bytes()),
64 ).unwrap()
65 }
66
67 pub fn verify_token<T>(&self, token: &str) -> bool
68 where
69 T: for<'de> Deserialize<'de> + Clone,
70 {
71 let validation = Validation::default();
72 let result = decode::<Claims<T>>(
73 token,
74 &DecodingKey::from_secret(self.secret_key.as_bytes()),
75 &validation,
76 );
77 result.is_ok()
78 }
79
80 pub fn is_token_expired<T>(&self, token: &str) -> bool
81 where
82 T: for<'de> Deserialize<'de> + Clone,
83 {
84 let validation = Validation::default();
85 if let Ok(data) = decode::<Claims<T>>(
86 token,
87 &DecodingKey::from_secret(self.secret_key.as_bytes()),
88 &validation,
89 ) {
90 let now = Utc::now().timestamp() as usize;
91 data.claims.exp < now
92 } else {
93 true }
95 }
96
97 pub fn encrypt(&self, input: &str) -> Result<String, Box<dyn Error>> {
98
99 let key_bytes = derive_key_from_string(&self.encryption_key);
100 let key = GenericArray::from_slice(&key_bytes);
101 let cipher = Aes256Gcm::new(key);
102
103 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
104
105 let cipher_text = cipher.encrypt(&nonce, input.as_bytes())
106 .map_err(|e| format!("Encryption failed: {}", e))?;
107
108 let mut encrypted_data = nonce.to_vec();
109 encrypted_data.extend_from_slice(&cipher_text);
110
111 Ok(base64::engine::general_purpose::STANDARD.encode(encrypted_data))
112 }
113
114 pub fn decrypt(&self, input: &str) -> Result<String, Box<dyn Error>> {
115
116 let key_bytes = derive_key_from_string(&self.encryption_key);
117 let key = GenericArray::from_slice(&key_bytes);
118 let cipher = Aes256Gcm::new(key);
119
120 let encrypted_data = base64::engine::general_purpose::STANDARD.decode(input)
121 .map_err(|e| format!("Base64 decode failed: {}", e))?;
122
123 if encrypted_data.len() < 12 {
124 return Err("Invalid encrypted data: too short".into());
125 }
126
127 let (nonce_bytes, cipher_text) = encrypted_data.split_at(12);
128 let nonce = Nonce::from_slice(nonce_bytes);
129
130 let plaintext = cipher.decrypt(nonce, cipher_text)
131 .map_err(|e| format!("Decryption failed: {}", e))?;
132
133 String::from_utf8(plaintext)
134 .map_err(|e| format!("Invalid UTF-8: {}", e).into())
135 }
136}