1use argon2::{
4 password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
5 Argon2,
6};
7use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
8use serde::{Deserialize, Serialize};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11#[derive(Debug, Serialize, Deserialize)]
13pub struct AdminClaims {
14 pub sub: String,
16 pub exp: usize,
18 pub iat: usize,
20}
21
22pub fn hash_password(password: &str) -> Result<String, String> {
24 let salt = SaltString::generate(&mut OsRng);
25 let argon2 = Argon2::default();
26 let hash = argon2
27 .hash_password(password.as_bytes(), &salt)
28 .map_err(|e| format!("hashing failed: {}", e))?;
29 Ok(hash.to_string())
30}
31
32pub fn verify_password(password: &str, hash: &str) -> Result<bool, String> {
34 let parsed =
35 PasswordHash::new(hash).map_err(|e| format!("invalid hash format: {}", e))?;
36 Ok(Argon2::default()
37 .verify_password(password.as_bytes(), &parsed)
38 .is_ok())
39}
40
41pub fn create_jwt(username: &str, secret: &str) -> Result<String, String> {
43 let now = SystemTime::now()
44 .duration_since(UNIX_EPOCH)
45 .map_err(|e| format!("time error: {}", e))?
46 .as_secs() as usize;
47
48 let claims = AdminClaims {
49 sub: username.to_string(),
50 iat: now,
51 exp: now + 86400,
52 };
53
54 encode(
55 &Header::default(),
56 &claims,
57 &EncodingKey::from_secret(secret.as_bytes()),
58 )
59 .map_err(|e| format!("jwt encoding failed: {}", e))
60}
61
62pub fn verify_jwt(token: &str, secret: &str) -> Result<AdminClaims, String> {
64 let token_data = decode::<AdminClaims>(
65 token,
66 &DecodingKey::from_secret(secret.as_bytes()),
67 &Validation::default(),
68 )
69 .map_err(|e| format!("jwt verification failed: {}", e))?;
70 Ok(token_data.claims)
71}