Skip to main content

auth_framework/server/oauth/
oauth2_enhanced_storage.rs

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