auth_framework/security/
secure_jwt.rs

1use crate::errors::Result;
2use jsonwebtoken::{Algorithm, DecodingKey};
3use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5use std::time::Duration;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct SecureJwtClaims {
9    pub sub: String,
10    pub iss: String,
11    pub aud: String,
12    pub exp: i64,
13    pub nbf: i64,
14    pub iat: i64,
15    pub jti: String,
16    pub scope: String,
17    pub typ: String,
18    pub sid: Option<String>,
19    pub client_id: Option<String>,
20    pub auth_ctx_hash: Option<String>,
21}
22
23#[derive(Debug, Clone)]
24pub struct SecureJwtConfig {
25    pub allowed_algorithms: Vec<Algorithm>,
26    pub required_issuers: HashSet<String>,
27    pub required_audiences: HashSet<String>,
28    pub max_token_lifetime: Duration,
29    pub clock_skew: Duration,
30    pub require_jti: bool,
31    pub validate_nbf: bool,
32    pub allowed_token_types: HashSet<String>,
33    pub require_secure_transport: bool,
34    /// JWT signing/validation key
35    pub jwt_secret: String,
36}
37
38impl Default for SecureJwtConfig {
39    fn default() -> Self {
40        let mut allowed_token_types = HashSet::new();
41        allowed_token_types.insert("access".to_string());
42        allowed_token_types.insert("refresh".to_string());
43        allowed_token_types.insert("JARM".to_string());
44
45        let mut required_issuers = HashSet::new();
46        required_issuers.insert("auth-framework".to_string());
47
48        Self {
49            allowed_algorithms: vec![Algorithm::HS256, Algorithm::RS256, Algorithm::ES256],
50            required_issuers,
51            required_audiences: HashSet::new(),
52            max_token_lifetime: Duration::from_secs(3600),
53            clock_skew: Duration::from_secs(30),
54            require_jti: true,
55            validate_nbf: true,
56            allowed_token_types,
57            require_secure_transport: false,
58            jwt_secret: "CHANGE_THIS_IN_PRODUCTION_USE_PROPER_KEY_MANAGEMENT".to_string(),
59        }
60    }
61}
62
63#[derive(Debug)]
64pub struct SecureJwtValidator {
65    config: SecureJwtConfig,
66    revoked_tokens: std::sync::Mutex<std::collections::HashSet<String>>,
67}
68
69impl SecureJwtValidator {
70    pub fn new(config: SecureJwtConfig) -> Self {
71        Self {
72            config,
73            revoked_tokens: std::sync::Mutex::new(std::collections::HashSet::new()),
74        }
75    }
76
77    /// Get decoding key for JWT validation
78    pub fn get_decoding_key(&self) -> jsonwebtoken::DecodingKey {
79        jsonwebtoken::DecodingKey::from_secret(self.config.jwt_secret.as_bytes())
80    }
81
82    pub fn validate_token(
83        &self,
84        token: &str,
85        decoding_key: &DecodingKey,
86        verify_signature: bool,
87    ) -> Result<SecureJwtClaims> {
88        use jsonwebtoken::{Algorithm, Validation, decode};
89
90        // Create validation with signature verification
91        let mut validation = Validation::new(Algorithm::HS256);
92        validation.validate_exp = false; // Let our custom validation handle expiry
93        validation.validate_aud = false; // Let our custom validation handle audience
94        validation.validate_nbf = false; // Let our custom validation handle not before
95
96        if !verify_signature {
97            validation.insecure_disable_signature_validation();
98        }
99
100        // Decode and validate the JWT
101        match decode::<SecureJwtClaims>(token, decoding_key, &validation) {
102            Ok(token_data) => {
103                let claims = token_data.claims;
104
105                // Check if token is revoked
106                if self.is_token_revoked(&claims.jti)? {
107                    return Err(crate::errors::AuthError::Unauthorized(
108                        "Token is revoked".to_string(),
109                    ));
110                }
111
112                // Additional custom validations can be added here
113
114                Ok(claims)
115            }
116            Err(e) => Err(crate::errors::AuthError::Unauthorized(format!(
117                "JWT validation failed: {}",
118                e
119            ))),
120        }
121    }
122
123    pub fn is_token_revoked(&self, jti: &str) -> Result<bool> {
124        let revoked_tokens = self.revoked_tokens.lock().unwrap();
125        Ok(revoked_tokens.contains(jti))
126    }
127
128    pub fn revoke_token(&self, jti: &str) -> Result<()> {
129        let mut revoked_tokens = self.revoked_tokens.lock().unwrap();
130        revoked_tokens.insert(jti.to_string());
131        Ok(())
132    }
133
134    pub fn cleanup_revoked_tokens(&self, _expired_cutoff: std::time::SystemTime) -> Result<()> {
135        // For production, this would actually clean up expired tokens
136        // For testing, we'll just keep them all for simplicity
137        Ok(())
138    }
139}
140
141