// src/auth/jwt.rs use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation}; use serde::{Deserialize, Serialize}; use time::{Duration, OffsetDateTime}; use uuid::Uuid; use std::sync::OnceLock; use crate::auth::models::AuthError; static KEYS: OnceLock = OnceLock::new(); struct Keys { encoding: EncodingKey, decoding: DecodingKey, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Claims { pub sub: Uuid, // User ID pub exp: i64, // Expiration time pub role: String, // User role } pub fn init_jwt() -> Result<(), AuthError> { let secret = std::env::var("JWT_SECRET") .map_err(|_| AuthError::ConfigError("JWT_SECRET must be set".to_string()))?; KEYS.set(Keys { encoding: EncodingKey::from_secret(secret.as_bytes()), decoding: DecodingKey::from_secret(secret.as_bytes()), }).map_err(|_| AuthError::ConfigError("Failed to initialize JWT keys".to_string()))?; Ok(()) } pub fn generate_token(user_id: Uuid, role: &str) -> Result { let keys = KEYS.get().ok_or(AuthError::ConfigError("JWT not initialized".to_string()))?; let exp = OffsetDateTime::now_utc() + Duration::hours(24); let claims = Claims { sub: user_id, exp: exp.unix_timestamp(), role: role.to_string(), }; encode(&Header::default(), &claims, &keys.encoding) .map_err(|e| AuthError::JwtError(e.to_string())) } pub fn validate_token(token: &str) -> Result { let keys = KEYS.get().ok_or(AuthError::ConfigError("JWT not initialized".to_string()))?; decode::(token, &keys.decoding, &Validation::default()) .map(|data| data.claims) .map_err(|e| AuthError::JwtError(e.to_string())) }