auth_framework/
oauth2_enhanced_storage.rs

1//! Enhanced OAuth2 token and code storage with proper validation
2
3use crate::errors::Result;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::time::SystemTime;
7use uuid::Uuid;
8
9/// Stored refresh token with validation metadata
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct RefreshToken {
12    pub token_id: String,
13    pub client_id: String,
14    pub user_id: String,
15    pub scopes: Vec<String>,
16    pub issued_at: SystemTime,
17    pub expires_at: SystemTime,
18    pub is_revoked: bool,
19}
20
21impl RefreshToken {
22    pub fn new(
23        client_id: String,
24        user_id: String,
25        scopes: Vec<String>,
26        lifetime: std::time::Duration,
27    ) -> Self {
28        let now = SystemTime::now();
29        Self {
30            token_id: Uuid::new_v4().to_string(),
31            client_id,
32            user_id,
33            scopes,
34            issued_at: now,
35            expires_at: now + lifetime,
36            is_revoked: false,
37        }
38    }
39
40    pub fn is_expired(&self) -> bool {
41        SystemTime::now() > self.expires_at
42    }
43
44    pub fn is_valid(&self) -> bool {
45        !self.is_revoked && !self.is_expired()
46    }
47
48    pub fn revoke(&mut self) {
49        self.is_revoked = true;
50    }
51}
52
53/// Enhanced authorization code with user context
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct EnhancedAuthorizationCode {
56    pub code: String,
57    pub client_id: String,
58    pub user_id: String,
59    pub redirect_uri: String,
60    pub scopes: Vec<String>,
61    pub code_challenge: Option<String>,
62    pub code_challenge_method: Option<String>,
63    pub issued_at: SystemTime,
64    pub expires_at: SystemTime,
65    pub is_used: bool,
66}
67
68impl EnhancedAuthorizationCode {
69    pub fn new(
70        client_id: String,
71        user_id: String,
72        redirect_uri: String,
73        scopes: Vec<String>,
74        code_challenge: Option<String>,
75        code_challenge_method: Option<String>,
76        lifetime: std::time::Duration,
77    ) -> Self {
78        let now = SystemTime::now();
79        Self {
80            code: Uuid::new_v4().to_string(),
81            client_id,
82            user_id,
83            redirect_uri,
84            scopes,
85            code_challenge,
86            code_challenge_method,
87            issued_at: now,
88            expires_at: now + lifetime,
89            is_used: false,
90        }
91    }
92
93    pub fn is_expired(&self) -> bool {
94        SystemTime::now() > self.expires_at
95    }
96
97    pub fn is_valid(&self) -> bool {
98        !self.is_used && !self.is_expired()
99    }
100
101    pub fn mark_used(&mut self) {
102        self.is_used = true;
103    }
104}
105
106/// Enhanced client credentials with proper secret validation
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct EnhancedClientCredentials {
109    pub client_id: String,
110    pub client_secret_hash: String,
111    pub client_type: ClientType,
112    pub redirect_uris: Vec<String>,
113    pub allowed_scopes: Vec<String>,
114    pub grant_types: Vec<String>,
115    pub created_at: SystemTime,
116    pub is_active: bool,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub enum ClientType {
121    Confidential,
122    Public,
123}
124
125impl EnhancedClientCredentials {
126    pub fn new_confidential(
127        client_id: String,
128        client_secret: &str,
129        redirect_uris: Vec<String>,
130        allowed_scopes: Vec<String>,
131        grant_types: Vec<String>,
132    ) -> Result<Self> {
133        use crate::security::secure_utils::hash_password;
134
135        Ok(Self {
136            client_id,
137            client_secret_hash: hash_password(client_secret)?,
138            client_type: ClientType::Confidential,
139            redirect_uris,
140            allowed_scopes,
141            grant_types,
142            created_at: SystemTime::now(),
143            is_active: true,
144        })
145    }
146
147    pub fn new_public(
148        client_id: String,
149        redirect_uris: Vec<String>,
150        allowed_scopes: Vec<String>,
151        grant_types: Vec<String>,
152    ) -> Self {
153        Self {
154            client_id,
155            client_secret_hash: String::new(), // Public clients don't have secrets
156            client_type: ClientType::Public,
157            redirect_uris,
158            allowed_scopes,
159            grant_types,
160            created_at: SystemTime::now(),
161            is_active: true,
162        }
163    }
164
165    pub fn validate_secret(&self, provided_secret: &str) -> Result<bool> {
166        match self.client_type {
167            ClientType::Public => Ok(true), // Public clients don't need secret validation
168            ClientType::Confidential => {
169                use crate::security::secure_utils::verify_password;
170                verify_password(provided_secret, &self.client_secret_hash)
171            }
172        }
173    }
174
175    pub fn requires_secret(&self) -> bool {
176        matches!(self.client_type, ClientType::Confidential)
177    }
178
179    pub fn has_scope(&self, scope: &str) -> bool {
180        self.allowed_scopes.contains(&scope.to_string())
181    }
182
183    pub fn supports_grant_type(&self, grant_type: &str) -> bool {
184        self.grant_types.contains(&grant_type.to_string())
185    }
186}
187
188/// Enhanced token storage with proper validation
189#[derive(Debug, Clone)]
190pub struct EnhancedTokenStorage {
191    refresh_tokens: HashMap<String, RefreshToken>,
192    authorization_codes: HashMap<String, EnhancedAuthorizationCode>,
193    client_credentials: HashMap<String, EnhancedClientCredentials>,
194}
195
196impl EnhancedTokenStorage {
197    pub fn new() -> Self {
198        Self {
199            refresh_tokens: HashMap::new(),
200            authorization_codes: HashMap::new(),
201            client_credentials: HashMap::new(),
202        }
203    }
204
205    // Refresh token operations
206    pub async fn store_refresh_token(&mut self, token: RefreshToken) -> Result<String> {
207        let token_id = token.token_id.clone();
208        self.refresh_tokens.insert(token_id.clone(), token);
209        Ok(token_id)
210    }
211
212    pub async fn get_refresh_token(&self, token_id: &str) -> Result<Option<RefreshToken>> {
213        Ok(self.refresh_tokens.get(token_id).cloned())
214    }
215
216    pub async fn validate_refresh_token(&self, token_id: &str) -> Result<bool> {
217        match self.refresh_tokens.get(token_id) {
218            Some(token) => Ok(token.is_valid()),
219            None => Ok(false),
220        }
221    }
222
223    pub async fn revoke_refresh_token(&mut self, token_id: &str) -> Result<bool> {
224        match self.refresh_tokens.get_mut(token_id) {
225            Some(token) => {
226                token.revoke();
227                Ok(true)
228            }
229            None => Ok(false),
230        }
231    }
232
233    // Authorization code operations
234    pub async fn store_authorization_code(
235        &mut self,
236        code: EnhancedAuthorizationCode,
237    ) -> Result<String> {
238        let code_value = code.code.clone();
239        self.authorization_codes.insert(code_value.clone(), code);
240        Ok(code_value)
241    }
242
243    pub async fn get_authorization_code(
244        &self,
245        code: &str,
246    ) -> Result<Option<EnhancedAuthorizationCode>> {
247        Ok(self.authorization_codes.get(code).cloned())
248    }
249
250    pub async fn consume_authorization_code(
251        &mut self,
252        code: &str,
253    ) -> Result<Option<EnhancedAuthorizationCode>> {
254        match self.authorization_codes.get_mut(code) {
255            Some(auth_code) if auth_code.is_valid() => {
256                auth_code.mark_used();
257                Ok(Some(auth_code.clone()))
258            }
259            _ => Ok(None),
260        }
261    }
262
263    // Client credentials operations
264    pub async fn store_client_credentials(
265        &mut self,
266        credentials: EnhancedClientCredentials,
267    ) -> Result<()> {
268        let client_id = credentials.client_id.clone();
269        self.client_credentials.insert(client_id, credentials);
270        Ok(())
271    }
272
273    pub async fn get_client_credentials(
274        &self,
275        client_id: &str,
276    ) -> Result<Option<EnhancedClientCredentials>> {
277        Ok(self.client_credentials.get(client_id).cloned())
278    }
279
280    pub async fn validate_client_credentials(
281        &self,
282        client_id: &str,
283        client_secret: Option<&str>,
284    ) -> Result<bool> {
285        match self.client_credentials.get(client_id) {
286            Some(credentials) if credentials.is_active => {
287                if credentials.requires_secret() {
288                    match client_secret {
289                        Some(secret) => credentials.validate_secret(secret),
290                        None => Ok(false), // Secret required but not provided
291                    }
292                } else {
293                    Ok(true) // Public client, no secret required
294                }
295            }
296            _ => Ok(false), // Client not found or inactive
297        }
298    }
299
300    // Cleanup operations
301    pub async fn cleanup_expired_tokens(&mut self) -> Result<usize> {
302        let initial_count = self.refresh_tokens.len() + self.authorization_codes.len();
303
304        // Remove expired refresh tokens
305        self.refresh_tokens.retain(|_, token| token.is_valid());
306
307        // Remove expired authorization codes
308        self.authorization_codes.retain(|_, code| code.is_valid());
309
310        let final_count = self.refresh_tokens.len() + self.authorization_codes.len();
311        Ok(initial_count - final_count)
312    }
313
314    // User credential management methods
315
316    /// Get user credentials for authentication
317    pub async fn get_user_credentials(&self, username: &str) -> Result<Option<UserCredentials>> {
318        // For demo purposes, use pre-computed bcrypt hashes for known users
319        // In production, this would query a database with pre-hashed passwords
320        let demo_users = [
321            (
322                "admin",
323                "$2b$12$UfM9FwL8dbLOFTOmL8kAVOUe8.mFYGsqtaEjpMrS6yOGhN6SHL6me", // hash of "admin_password_123456789"
324                vec!["read", "write", "admin", "delete"],
325            ),
326            (
327                "user",
328                "$2b$12$3Jb05MyZ7QDS81DJkt3QLeyR9z.S9yQqULr42kZ5F5crUYbwJaXdW", // hash of "user_password_123456789"
329                vec!["read", "write"],
330            ),
331            (
332                "test",
333                "$2b$12$bM6NV4EQdo8kJHUhhZGpIuIsalt4eD9J8co0KyO6pzEPX0ClONwTy", // hash of "test_password_123456789"
334                vec!["read"],
335            ),
336        ];
337
338        for (demo_user, password_hash, demo_scopes) in &demo_users {
339            if username == *demo_user {
340                return Ok(Some(UserCredentials {
341                    username: username.to_string(),
342                    password_hash: password_hash.to_string(),
343                    scopes: demo_scopes.iter().map(|s| s.to_string()).collect(),
344                    is_active: true,
345                }));
346            }
347        }
348
349        Ok(None)
350    }
351
352    /// Get user permissions/scopes
353    pub async fn get_user_permissions(&self, username: &str) -> Result<Option<UserPermissions>> {
354        // Get credentials which include permissions
355        if let Some(credentials) = self.get_user_credentials(username).await? {
356            Ok(Some(UserPermissions {
357                username: credentials.username,
358                scopes: credentials.scopes,
359                is_active: credentials.is_active,
360            }))
361        } else {
362            Ok(None)
363        }
364    }
365}
366
367/// User credentials stored in the system
368#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct UserCredentials {
370    pub username: String,
371    pub password_hash: String, // In production: bcrypt/argon2 hash
372    pub scopes: Vec<String>,
373    pub is_active: bool,
374}
375
376/// User permissions/scopes
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct UserPermissions {
379    pub username: String,
380    pub scopes: Vec<String>,
381    pub is_active: bool,
382}
383
384impl Default for EnhancedTokenStorage {
385    fn default() -> Self {
386        Self::new()
387    }
388}
389
390