oxidite_auth/
jwt.rs

1use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
2use serde::{Deserialize, Serialize};
3use std::time::{SystemTime, UNIX_EPOCH};
4use crate::Result;
5
6/// JWT Claims structure
7#[derive(Debug, Serialize, Deserialize, Clone)]
8pub struct Claims {
9    pub sub: String,  // Subject (user ID)
10    pub exp: usize,   // Expiration time
11    pub iat: usize,   // Issued at
12    pub nbf: usize,   // Not before
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub roles: Option<Vec<String>>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub permissions: Option<Vec<String>>,
17}
18
19impl Claims {
20    pub fn new(user_id: String, expiry_secs: u64) -> Self {
21        let now = SystemTime::now()
22            .duration_since(UNIX_EPOCH)
23            .unwrap()
24            .as_secs() as usize;
25
26        Self {
27            sub: user_id,
28            exp: now + expiry_secs as usize,
29            iat: now,
30            nbf: now,
31            roles: None,
32            permissions: None,
33        }
34    }
35
36    pub fn with_roles(mut self, roles: Vec<String>) -> Self {
37        self.roles = Some(roles);
38        self
39    }
40
41    pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
42        self.permissions = Some(permissions);
43        self
44    }
45
46    pub fn has_role(&self, role: &str) -> bool {
47        self.roles
48            .as_ref()
49            .map(|roles| roles.iter().any(|r| r == role))
50            .unwrap_or(false)
51    }
52
53    pub fn has_permission(&self, permission: &str) -> bool {
54        self.permissions
55            .as_ref()
56            .map(|perms| perms.iter().any(|p| p == permission))
57            .unwrap_or(false)
58    }
59}
60
61/// JWT Manager for creating and verifying tokens
62pub struct JwtManager {
63    secret: String,
64}
65
66impl JwtManager {
67    pub fn new(secret: String) -> Self {
68        Self { secret }
69    }
70
71    pub fn generate_token<T: Serialize>(&self, claims: &T) -> Result<String> {
72        let token = encode(
73            &Header::default(),
74            claims,
75            &EncodingKey::from_secret(self.secret.as_bytes()),
76        )?;
77        Ok(token)
78    }
79
80    pub fn verify(&self, token: &str) -> Result<Claims> {
81        let token_data = decode::<Claims>(
82            token,
83            &DecodingKey::from_secret(self.secret.as_bytes()),
84            &Validation::default(),
85        )?;
86        Ok(token_data.claims)
87    }
88}
89
90/// Create a JWT token
91pub fn create_token(user_id: String, secret: &str, expiry_secs: u64) -> Result<String> {
92    let claims = Claims::new(user_id, expiry_secs);
93    let jwt = JwtManager::new(secret.to_string());
94    jwt.generate_token(&claims)
95}
96
97/// Verify a JWT token
98pub fn verify_token(token: &str, secret: &str) -> Result<Claims> {
99    let jwt = JwtManager::new(secret.to_string());
100    jwt.verify(token)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_create_and_verify_token() {
109        let secret = "test_secret_key";
110        let user_id = "user123";
111        
112        let token = create_token(user_id.to_string(), secret, 3600).unwrap();
113        let claims = verify_token(&token, secret).unwrap();
114        
115        assert_eq!(claims.sub, user_id);
116    }
117
118    #[test]
119    fn test_claims_with_roles() {
120        let claims = Claims::new("user123".to_string(), 3600)
121            .with_roles(vec!["admin".to_string(), "user".to_string()]);
122        
123        assert!(claims.has_role("admin"));
124        assert!(claims.has_role("user"));
125        assert!(!claims.has_role("guest"));
126    }
127}