auth_framework/methods/
mod.rs

1//! Authentication method implementations.
2
3use crate::{
4    authentication::credentials::{Credential, CredentialMetadata},
5    errors::{AuthError, Result},
6    tokens::AuthToken,
7};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11// Import the specific auth method modules
12pub mod enhanced_device;
13pub mod hardware_token;
14pub mod passkey;
15#[cfg(feature = "saml")]
16pub mod saml;
17
18// Re-export types from submodules
19#[cfg(feature = "enhanced-device-flow")]
20pub use enhanced_device::EnhancedDeviceFlowMethod;
21pub use hardware_token::HardwareToken;
22#[cfg(feature = "passkeys")]
23pub use passkey::PasskeyAuthMethod;
24#[cfg(feature = "saml")]
25pub use saml::SamlAuthMethod;
26
27/// Result of an authentication attempt.
28#[derive(Debug, Clone)]
29pub enum MethodResult {
30    /// Authentication was successful
31    Success(Box<AuthToken>),
32
33    /// Multi-factor authentication is required
34    MfaRequired(Box<MfaChallenge>),
35
36    /// Authentication failed
37    Failure { reason: String },
38}
39
40/// Multi-factor authentication challenge.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct MfaChallenge {
43    /// Unique challenge ID
44    pub id: String,
45
46    /// Type of MFA required
47    pub mfa_type: MfaType,
48
49    /// User ID this challenge is for
50    pub user_id: String,
51
52    /// When the challenge expires
53    pub expires_at: chrono::DateTime<chrono::Utc>,
54
55    /// Optional message or instructions
56    pub message: Option<String>,
57
58    /// Additional challenge data
59    pub data: HashMap<String, serde_json::Value>,
60}
61
62/// Types of multi-factor authentication.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub enum MfaType {
65    /// Time-based one-time password (TOTP)
66    Totp,
67
68    /// SMS verification code
69    Sms { phone_number: String },
70
71    /// Email verification code
72    Email { email_address: String },
73
74    /// Push notification
75    Push { device_id: String },
76
77    /// Hardware security key
78    SecurityKey,
79
80    /// Backup codes
81    BackupCode,
82}
83
84/// Trait for authentication methods.
85pub trait AuthMethod: Send + Sync {
86    type MethodResult: Send + Sync + 'static;
87    type AuthToken: Send + Sync + 'static;
88
89    /// Get the name of this authentication method.
90    fn name(&self) -> &str;
91
92    /// Authenticate using the provided credentials.
93    fn authenticate(
94        &self,
95        credential: Credential,
96        metadata: CredentialMetadata,
97    ) -> impl std::future::Future<Output = Result<Self::MethodResult>> + Send;
98
99    /// Validate configuration for this method.
100    fn validate_config(&self) -> Result<()>;
101
102    /// Check if this method supports refresh tokens.
103    fn supports_refresh(&self) -> bool {
104        false
105    }
106
107    /// Refresh a token if supported.
108    fn refresh_token(
109        &self,
110        _refresh_token: String,
111    ) -> impl std::future::Future<Output = Result<AuthToken, AuthError>> + Send {
112        async {
113            Err(AuthError::auth_method(
114                self.name(),
115                "Token refresh not supported by this method".to_string(),
116            ))
117        }
118    }
119}
120
121/// Basic user information.
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct UserInfo {
124    /// User ID
125    pub id: String,
126
127    /// Username
128    pub username: String,
129
130    /// Email address
131    pub email: Option<String>,
132
133    /// Display name
134    pub name: Option<String>,
135
136    /// User roles
137    pub roles: Vec<String>,
138
139    /// Whether the user is active
140    pub active: bool,
141
142    /// Additional user attributes
143    pub attributes: HashMap<String, serde_json::Value>,
144}
145
146/// Enum wrapper for all supported authentication methods (for registry)
147pub enum AuthMethodEnum {
148    Password(PasswordMethod),
149    Jwt(JwtMethod),
150    ApiKey(ApiKeyMethod),
151    OAuth2(OAuth2Method),
152    #[cfg(feature = "saml")]
153    Saml(SamlAuthMethod),
154    #[cfg(feature = "ldap-auth")]
155    Ldap(LdapAuthMethod),
156    HardwareToken(HardwareToken),
157    OpenIdConnect(OpenIdConnectAuthMethod),
158    AdvancedMfa(AdvancedMfaAuthMethod),
159    #[cfg(feature = "enhanced-device-flow")]
160    EnhancedDeviceFlow(Box<enhanced_device::EnhancedDeviceFlowMethod>),
161    #[cfg(feature = "passkeys")]
162    Passkey(PasskeyAuthMethod),
163}
164
165/// Password-based authentication method with secure credential storage
166#[derive(Clone)]
167pub struct PasswordMethod {
168    storage: std::sync::Arc<dyn crate::storage::AuthStorage>,
169}
170
171impl std::fmt::Debug for PasswordMethod {
172    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173        f.debug_struct("PasswordMethod")
174            .field("storage", &"<AuthStorage>")
175            .finish()
176    }
177}
178
179impl PasswordMethod {
180    pub fn new() -> Self {
181        // Create with memory storage by default - should be replaced with proper storage
182        Self {
183            storage: std::sync::Arc::new(crate::storage::MemoryStorage::new()),
184        }
185    }
186
187    pub fn with_storage(storage: std::sync::Arc<dyn crate::storage::AuthStorage>) -> Self {
188        Self { storage }
189    }
190
191    /// Authenticate user with username and password
192    async fn authenticate_password(&self, username: &str, password: &str) -> Result<AuthToken> {
193        use crate::utils::password::verify_password;
194
195        // Retrieve stored user credentials from storage
196        let user_key = format!("user:credentials:{}", username);
197        let stored_data = match self.storage.get_kv(&user_key).await? {
198            Some(data) => data,
199            None => {
200                // User not found - still do dummy password verification to prevent timing attacks
201                let _ = verify_password(
202                    password,
203                    "$2b$12$KIXXvZ4LmVYC3qj6RcZ5dO1WNVu8p5xGqF1Y5z6MhQp5z6MhQp5z6",
204                );
205                return Err(AuthError::auth_method(
206                    "password",
207                    "Invalid username or password".to_string(),
208                ));
209            }
210        };
211
212        // Parse stored user data
213        let user_data_str = String::from_utf8(stored_data)
214            .map_err(|e| AuthError::internal(format!("Failed to parse user data: {}", e)))?;
215
216        let user_data: serde_json::Value = serde_json::from_str(&user_data_str)
217            .map_err(|e| AuthError::internal(format!("Failed to parse user JSON: {}", e)))?;
218
219        // Extract password hash
220        let password_hash = user_data["password_hash"]
221            .as_str()
222            .ok_or_else(|| AuthError::internal("Missing password hash".to_string()))?;
223
224        // Verify password
225        let is_valid = verify_password(password, password_hash)
226            .map_err(|e| AuthError::crypto(format!("Password verification failed: {}", e)))?;
227
228        if !is_valid {
229            return Err(AuthError::auth_method(
230                "password",
231                "Invalid username or password".to_string(),
232            ));
233        }
234
235        // Extract user information
236        let user_id = user_data["user_id"]
237            .as_str()
238            .ok_or_else(|| AuthError::internal("Missing user_id".to_string()))?
239            .to_string();
240
241        let email = user_data["email"].as_str().map(|s| s.to_string());
242
243        // Create authentication token with all required fields
244        let now = chrono::Utc::now();
245        let token_id = uuid::Uuid::new_v4().to_string();
246        let access_token = uuid::Uuid::new_v4().to_string(); // Generate a unique access token
247
248        let token = AuthToken {
249            token_id: token_id.clone(),
250            user_id: user_id.clone(),
251            access_token,
252            token_type: Some("bearer".to_string()),
253            subject: Some(username.to_string()),
254            issuer: Some("auth-framework".to_string()),
255            refresh_token: None,
256            issued_at: now,
257            expires_at: now + chrono::Duration::hours(24),
258            scopes: vec!["read".to_string(), "write".to_string()],
259            auth_method: "password".to_string(),
260            client_id: None,
261            user_profile: email.map(|e| crate::providers::UserProfile {
262                id: Some(user_id.clone()),
263                provider: Some("password".to_string()),
264                username: Some(username.to_string()),
265                name: Some(username.to_string()),
266                email: Some(e),
267                email_verified: Some(false), // Default to unverified
268                picture: None,
269                locale: None,
270                additional_data: HashMap::new(),
271            }),
272            permissions: vec!["read".to_string(), "write".to_string()],
273            roles: vec!["user".to_string()],
274            metadata: crate::tokens::TokenMetadata {
275                issued_ip: None,
276                user_agent: None,
277                device_id: None,
278                session_id: Some(token_id.clone()),
279                revoked: false,
280                revoked_at: None,
281                revoked_reason: None,
282                last_used: Some(now),
283                use_count: 0,
284                custom: HashMap::new(),
285            },
286        };
287
288        tracing::info!("Password authentication successful for user: {}", username);
289        Ok(token)
290    }
291}
292
293impl Default for PasswordMethod {
294    fn default() -> Self {
295        Self::new()
296    }
297}
298
299// Stub implementations for other auth methods - to be implemented
300
301/// JWT-based authentication method
302#[derive(Clone)]
303pub struct JwtMethod {
304    token_manager: std::sync::Arc<crate::tokens::TokenManager>,
305}
306
307impl std::fmt::Debug for JwtMethod {
308    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309        f.debug_struct("JwtMethod")
310            .field("token_manager", &"<TokenManager>")
311            .finish()
312    }
313}
314
315impl JwtMethod {
316    pub fn new() -> Self {
317        // Create default token manager - should be replaced with proper configuration
318        let default_secret = b"temporary_jwt_secret_replace_in_production";
319        let token_manager = crate::tokens::TokenManager::new_hmac(
320            default_secret,
321            "auth-framework",
322            "auth-framework",
323        );
324        Self {
325            token_manager: std::sync::Arc::new(token_manager),
326        }
327    }
328
329    pub fn with_token_manager(token_manager: std::sync::Arc<crate::tokens::TokenManager>) -> Self {
330        Self { token_manager }
331    }
332
333    pub fn secret_key(self, _secret: &str) -> Self {
334        // Builder pattern - for compatibility, but use with_token_manager instead
335        self
336    }
337
338    pub fn issuer(self, _issuer: &str) -> Self {
339        // Builder pattern - for compatibility, but use with_token_manager instead
340        self
341    }
342
343    pub fn audience(self, _audience: &str) -> Self {
344        // Builder pattern - for compatibility, but use with_token_manager instead
345        self
346    }
347
348    /// Authenticate using JWT token
349    async fn authenticate_jwt(&self, token: &str) -> Result<AuthToken> {
350        // Validate JWT token using TokenManager
351        let jwt_claims = self
352            .token_manager
353            .validate_jwt_token(token)
354            .map_err(|e| AuthError::auth_method("jwt", format!("JWT validation failed: {}", e)))?;
355
356        // Extract user information from claims
357        let user_id = jwt_claims.sub.clone();
358
359        // Get additional user info from custom claims
360        let roles = jwt_claims
361            .custom
362            .get("roles")
363            .and_then(|v| v.as_array())
364            .map(|arr| {
365                arr.iter()
366                    .filter_map(|v| v.as_str().map(String::from))
367                    .collect()
368            })
369            .unwrap_or_else(|| vec!["user".to_string()]);
370
371        let permissions = jwt_claims
372            .custom
373            .get("permissions")
374            .and_then(|v| v.as_array())
375            .map(|arr| {
376                arr.iter()
377                    .filter_map(|v| v.as_str().map(String::from))
378                    .collect()
379            })
380            .unwrap_or_else(|| vec!["read".to_string()]);
381
382        // Create AuthToken from validated JWT
383        let now = chrono::Utc::now();
384        let expires_at = chrono::DateTime::from_timestamp(jwt_claims.exp, 0)
385            .unwrap_or_else(|| now + chrono::Duration::hours(1));
386
387        let auth_token = AuthToken {
388            token_id: jwt_claims
389                .custom
390                .get("jti")
391                .and_then(|v| v.as_str())
392                .unwrap_or(&user_id)
393                .to_string(),
394            user_id: user_id.clone(),
395            access_token: token.to_string(),
396            token_type: Some("bearer".to_string()),
397            subject: Some(user_id.clone()),
398            issuer: Some(jwt_claims.iss.clone()),
399            refresh_token: None,
400            issued_at: chrono::DateTime::from_timestamp(jwt_claims.iat, 0).unwrap_or(now),
401            expires_at,
402            scopes: vec!["read".to_string(), "write".to_string()],
403            auth_method: "jwt".to_string(),
404            client_id: None,
405            user_profile: None,
406            permissions,
407            roles,
408            metadata: crate::tokens::TokenMetadata {
409                issued_ip: None,
410                user_agent: None,
411                device_id: None,
412                session_id: Some(uuid::Uuid::new_v4().to_string()),
413                revoked: false,
414                revoked_at: None,
415                revoked_reason: None,
416                last_used: Some(now),
417                use_count: 0,
418                custom: HashMap::new(),
419            },
420        };
421
422        tracing::info!("JWT authentication successful for user: {}", user_id);
423        Ok(auth_token)
424    }
425}
426
427impl Default for JwtMethod {
428    fn default() -> Self {
429        Self::new()
430    }
431}
432
433/// API Key authentication method
434#[derive(Clone)]
435pub struct ApiKeyMethod {
436    storage: std::sync::Arc<dyn crate::storage::AuthStorage>,
437}
438
439impl std::fmt::Debug for ApiKeyMethod {
440    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441        f.debug_struct("ApiKeyMethod")
442            .field("storage", &"<AuthStorage>")
443            .finish()
444    }
445}
446
447impl ApiKeyMethod {
448    pub fn new() -> Self {
449        // Create with memory storage by default
450        Self {
451            storage: std::sync::Arc::new(crate::storage::MemoryStorage::new()),
452        }
453    }
454
455    pub fn with_storage(storage: std::sync::Arc<dyn crate::storage::AuthStorage>) -> Self {
456        Self { storage }
457    }
458
459    /// Authenticate using API key
460    async fn authenticate_api_key(&self, api_key: &str) -> Result<AuthToken> {
461        // Validate API key format
462        if api_key.len() < 16 {
463            return Err(AuthError::auth_method("api_key", "Invalid API key format"));
464        }
465
466        // Retrieve API key data from storage
467        let key_data_key = format!("api_key:{}", api_key);
468        let stored_data = match self.storage.get_kv(&key_data_key).await? {
469            Some(data) => data,
470            None => {
471                return Err(AuthError::auth_method(
472                    "api_key",
473                    "Invalid or expired API key",
474                ));
475            }
476        };
477
478        // Parse API key data
479        let key_data_str = String::from_utf8(stored_data)
480            .map_err(|e| AuthError::internal(format!("Failed to parse API key data: {}", e)))?;
481
482        let key_data: serde_json::Value = serde_json::from_str(&key_data_str)
483            .map_err(|e| AuthError::internal(format!("Failed to parse API key JSON: {}", e)))?;
484
485        // Check expiration
486        if let Some(expires_at_str) = key_data["expires_at"].as_str() {
487            let expires_at = chrono::DateTime::parse_from_rfc3339(expires_at_str)
488                .map_err(|e| AuthError::internal(format!("Invalid expiration date: {}", e)))?;
489            if chrono::Utc::now() > expires_at.with_timezone(&chrono::Utc) {
490                return Err(AuthError::auth_method("api_key", "API key has expired"));
491            }
492        }
493
494        // Extract user information
495        let user_id = key_data["user_id"]
496            .as_str()
497            .ok_or_else(|| AuthError::internal("Missing user_id in API key data"))?
498            .to_string();
499
500        let name = key_data["name"].as_str().map(String::from);
501
502        let scopes = key_data["scopes"]
503            .as_array()
504            .map(|arr| {
505                arr.iter()
506                    .filter_map(|v| v.as_str().map(String::from))
507                    .collect()
508            })
509            .unwrap_or_else(|| vec!["api_access".to_string()]);
510
511        let permissions = key_data["permissions"]
512            .as_array()
513            .map(|arr| {
514                arr.iter()
515                    .filter_map(|v| v.as_str().map(String::from))
516                    .collect()
517            })
518            .unwrap_or_else(|| vec!["read".to_string()]);
519
520        // Update last used timestamp
521        let mut updated_data = key_data.clone();
522        updated_data["last_used"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
523        if let Some(count) = updated_data["use_count"].as_u64() {
524            updated_data["use_count"] = serde_json::json!(count + 1);
525        }
526
527        let updated_str = serde_json::to_string(&updated_data).unwrap_or(key_data_str);
528        let _ = self
529            .storage
530            .store_kv(&key_data_key, updated_str.as_bytes(), None)
531            .await;
532
533        // Create AuthToken
534        let now = chrono::Utc::now();
535        let auth_token = AuthToken {
536            token_id: uuid::Uuid::new_v4().to_string(),
537            user_id: user_id.clone(),
538            access_token: api_key.to_string(),
539            token_type: Some("api_key".to_string()),
540            subject: Some(user_id.clone()),
541            issuer: Some("auth-framework".to_string()),
542            refresh_token: None,
543            issued_at: now,
544            expires_at: if let Some(expires_at_str) = key_data["expires_at"].as_str() {
545                chrono::DateTime::parse_from_rfc3339(expires_at_str)
546                    .map(|dt| dt.with_timezone(&chrono::Utc))
547                    .unwrap_or_else(|_| now + chrono::Duration::days(365))
548            } else {
549                now + chrono::Duration::days(365)
550            },
551            scopes,
552            auth_method: "api_key".to_string(),
553            client_id: None,
554            user_profile: name.map(|n| crate::providers::UserProfile {
555                id: Some(user_id.clone()),
556                provider: Some("api_key".to_string()),
557                username: Some(user_id.clone()),
558                name: Some(n),
559                email: None,
560                email_verified: Some(false),
561                picture: None,
562                locale: None,
563                additional_data: HashMap::new(),
564            }),
565            permissions,
566            roles: vec!["api_user".to_string()],
567            metadata: crate::tokens::TokenMetadata {
568                issued_ip: None,
569                user_agent: None,
570                device_id: None,
571                session_id: Some(uuid::Uuid::new_v4().to_string()),
572                revoked: false,
573                revoked_at: None,
574                revoked_reason: None,
575                last_used: Some(now),
576                use_count: key_data["use_count"].as_u64().unwrap_or(0),
577                custom: HashMap::new(),
578            },
579        };
580
581        tracing::info!("API key authentication successful for user: {}", user_id);
582        Ok(auth_token)
583    }
584}
585
586impl Default for ApiKeyMethod {
587    fn default() -> Self {
588        Self::new()
589    }
590}
591
592/// OAuth2 authentication method
593#[derive(Clone)]
594pub struct OAuth2Method {
595    storage: std::sync::Arc<dyn crate::storage::AuthStorage>,
596    token_manager: std::sync::Arc<crate::tokens::TokenManager>,
597}
598
599impl std::fmt::Debug for OAuth2Method {
600    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601        f.debug_struct("OAuth2Method")
602            .field("storage", &"<AuthStorage>")
603            .field("token_manager", &"<TokenManager>")
604            .finish()
605    }
606}
607
608impl OAuth2Method {
609    pub fn new() -> Self {
610        let default_secret = b"temporary_oauth2_secret_replace_in_production";
611        let token_manager = crate::tokens::TokenManager::new_hmac(
612            default_secret,
613            "auth-framework",
614            "auth-framework",
615        );
616        Self {
617            storage: std::sync::Arc::new(crate::storage::MemoryStorage::new()),
618            token_manager: std::sync::Arc::new(token_manager),
619        }
620    }
621
622    pub fn with_storage_and_token_manager(
623        storage: std::sync::Arc<dyn crate::storage::AuthStorage>,
624        token_manager: std::sync::Arc<crate::tokens::TokenManager>,
625    ) -> Self {
626        Self {
627            storage,
628            token_manager,
629        }
630    }
631
632    /// Authenticate using OAuth2 access token
633    async fn authenticate_oauth2(&self, access_token: &str) -> Result<AuthToken> {
634        // For OAuth2, we validate the access token and retrieve user info
635
636        // First, try to validate as JWT (many OAuth2 providers use JWT access tokens)
637        if let Ok(jwt_claims) = self.token_manager.validate_jwt_token(access_token) {
638            // JWT-based OAuth2 token
639            let user_id = jwt_claims.sub.clone();
640            let now = chrono::Utc::now();
641
642            let auth_token = AuthToken {
643                token_id: uuid::Uuid::new_v4().to_string(),
644                user_id: user_id.clone(),
645                access_token: access_token.to_string(),
646                token_type: Some("bearer".to_string()),
647                subject: Some(user_id.clone()),
648                issuer: Some(jwt_claims.iss.clone()),
649                refresh_token: None,
650                issued_at: chrono::DateTime::from_timestamp(jwt_claims.iat, 0).unwrap_or(now),
651                expires_at: chrono::DateTime::from_timestamp(jwt_claims.exp, 0)
652                    .unwrap_or_else(|| now + chrono::Duration::hours(1)),
653                scopes: jwt_claims
654                    .custom
655                    .get("scope")
656                    .and_then(|v| v.as_str())
657                    .unwrap_or("openid profile email")
658                    .split_whitespace()
659                    .map(String::from)
660                    .collect(),
661                auth_method: "oauth2".to_string(),
662                client_id: jwt_claims
663                    .custom
664                    .get("client_id")
665                    .and_then(|v| v.as_str())
666                    .map(String::from),
667                user_profile: None,
668                permissions: vec!["read".to_string()],
669                roles: vec!["oauth_user".to_string()],
670                metadata: crate::tokens::TokenMetadata {
671                    issued_ip: None,
672                    user_agent: None,
673                    device_id: None,
674                    session_id: Some(uuid::Uuid::new_v4().to_string()),
675                    revoked: false,
676                    revoked_at: None,
677                    revoked_reason: None,
678                    last_used: Some(now),
679                    use_count: 0,
680                    custom: HashMap::new(),
681                },
682            };
683
684            tracing::info!("OAuth2 JWT authentication successful for user: {}", user_id);
685            return Ok(auth_token);
686        }
687
688        // Opaque token - look up in storage
689        let token_key = format!("oauth2_token:{}", access_token);
690        let stored_data = match self.storage.get_kv(&token_key).await? {
691            Some(data) => data,
692            None => {
693                return Err(AuthError::auth_method(
694                    "oauth2",
695                    "Invalid or expired OAuth2 token",
696                ));
697            }
698        };
699
700        // Parse token data
701        let token_data_str = String::from_utf8(stored_data).map_err(|e| {
702            AuthError::internal(format!("Failed to parse OAuth2 token data: {}", e))
703        })?;
704
705        let token_data: serde_json::Value = serde_json::from_str(&token_data_str).map_err(|e| {
706            AuthError::internal(format!("Failed to parse OAuth2 token JSON: {}", e))
707        })?;
708
709        // Check expiration
710        if let Some(expires_at_str) = token_data["expires_at"].as_str() {
711            let expires_at = chrono::DateTime::parse_from_rfc3339(expires_at_str)
712                .map_err(|e| AuthError::internal(format!("Invalid expiration date: {}", e)))?;
713            if chrono::Utc::now() > expires_at.with_timezone(&chrono::Utc) {
714                return Err(AuthError::auth_method("oauth2", "OAuth2 token has expired"));
715            }
716        }
717
718        // Extract user information
719        let user_id = token_data["user_id"]
720            .as_str()
721            .ok_or_else(|| AuthError::internal("Missing user_id in OAuth2 token data"))?
722            .to_string();
723
724        let scopes = token_data["scopes"]
725            .as_array()
726            .map(|arr| {
727                arr.iter()
728                    .filter_map(|v| v.as_str().map(String::from))
729                    .collect()
730            })
731            .unwrap_or_else(|| vec!["openid".to_string()]);
732
733        let now = chrono::Utc::now();
734        let auth_token = AuthToken {
735            token_id: uuid::Uuid::new_v4().to_string(),
736            user_id: user_id.clone(),
737            access_token: access_token.to_string(),
738            token_type: Some("bearer".to_string()),
739            subject: Some(user_id.clone()),
740            issuer: Some("oauth2_provider".to_string()),
741            refresh_token: token_data["refresh_token"].as_str().map(String::from),
742            issued_at: now,
743            expires_at: if let Some(expires_at_str) = token_data["expires_at"].as_str() {
744                chrono::DateTime::parse_from_rfc3339(expires_at_str)
745                    .map(|dt| dt.with_timezone(&chrono::Utc))
746                    .unwrap_or_else(|_| now + chrono::Duration::hours(1))
747            } else {
748                now + chrono::Duration::hours(1)
749            },
750            scopes,
751            auth_method: "oauth2".to_string(),
752            client_id: token_data["client_id"].as_str().map(String::from),
753            user_profile: None,
754            permissions: vec!["read".to_string()],
755            roles: vec!["oauth_user".to_string()],
756            metadata: crate::tokens::TokenMetadata {
757                issued_ip: None,
758                user_agent: None,
759                device_id: None,
760                session_id: Some(uuid::Uuid::new_v4().to_string()),
761                revoked: false,
762                revoked_at: None,
763                revoked_reason: None,
764                last_used: Some(now),
765                use_count: 0,
766                custom: HashMap::new(),
767            },
768        };
769
770        tracing::info!(
771            "OAuth2 opaque token authentication successful for user: {}",
772            user_id
773        );
774        Ok(auth_token)
775    }
776}
777
778impl Default for OAuth2Method {
779    fn default() -> Self {
780        Self::new()
781    }
782}
783
784#[cfg(feature = "ldap-auth")]
785#[derive(Debug)]
786pub struct LdapAuthMethod;
787
788#[derive(Debug)]
789pub struct OpenIdConnectAuthMethod;
790
791#[derive(Debug)]
792pub struct AdvancedMfaAuthMethod;
793
794// Implement AuthMethod trait specifically for PasswordMethod
795impl AuthMethod for PasswordMethod {
796    type MethodResult = MethodResult;
797    type AuthToken = AuthToken;
798
799    fn name(&self) -> &str {
800        "password"
801    }
802
803    async fn authenticate(
804        &self,
805        credential: Credential,
806        metadata: CredentialMetadata,
807    ) -> Result<Self::MethodResult> {
808        match credential {
809            Credential::Password { username, password } => {
810                // Validate inputs
811                if username.is_empty() || password.is_empty() {
812                    return Ok(MethodResult::Failure {
813                        reason: "Username or password cannot be empty".to_string(),
814                    });
815                }
816
817                // Log authentication attempt (without sensitive data)
818                tracing::info!(
819                    "Password authentication attempt for user: {} from IP: {:?}",
820                    username,
821                    metadata.client_ip
822                );
823
824                // Perform password authentication
825                match self.authenticate_password(&username, &password).await {
826                    Ok(token) => Ok(MethodResult::Success(Box::new(token))),
827                    Err(e) => {
828                        tracing::warn!(
829                            "Password authentication failed for user {}: {}",
830                            username,
831                            e
832                        );
833                        Ok(MethodResult::Failure {
834                            reason: "Invalid username or password".to_string(),
835                        })
836                    }
837                }
838            }
839            _ => Ok(MethodResult::Failure {
840                reason: "Password authentication requires username and password credentials"
841                    .to_string(),
842            }),
843        }
844    }
845
846    fn validate_config(&self) -> Result<()> {
847        // Validate that storage is accessible
848        // In a real implementation, we might check storage health here
849        Ok(())
850    }
851
852    fn supports_refresh(&self) -> bool {
853        false // Password auth doesn't support token refresh
854    }
855
856    async fn refresh_token(&self, _refresh_token: String) -> Result<AuthToken, AuthError> {
857        Err(AuthError::auth_method(
858            "password",
859            "Token refresh not supported for password authentication".to_string(),
860        ))
861    }
862}
863
864impl AuthMethod for AuthMethodEnum {
865    type MethodResult = MethodResult;
866    type AuthToken = AuthToken;
867
868    fn name(&self) -> &str {
869        match self {
870            AuthMethodEnum::Password(_) => "password",
871            AuthMethodEnum::Jwt(_) => "jwt",
872            AuthMethodEnum::ApiKey(_) => "api_key",
873            AuthMethodEnum::OAuth2(_) => "oauth2",
874            #[cfg(feature = "saml")]
875            AuthMethodEnum::Saml(_) => "saml",
876            #[cfg(feature = "ldap-auth")]
877            AuthMethodEnum::Ldap(_) => "ldap",
878            AuthMethodEnum::HardwareToken(_) => "hardware_token",
879            AuthMethodEnum::OpenIdConnect(_) => "openid_connect",
880            AuthMethodEnum::AdvancedMfa(_) => "advanced_mfa",
881            #[cfg(feature = "enhanced-device-flow")]
882            AuthMethodEnum::EnhancedDeviceFlow(_) => "enhanced_device_flow",
883            #[cfg(feature = "passkeys")]
884            AuthMethodEnum::Passkey(_) => "passkey",
885        }
886    }
887
888    async fn authenticate(
889        &self,
890        credential: Credential,
891        metadata: CredentialMetadata,
892    ) -> Result<Self::MethodResult> {
893        // Delegate to the concrete method implementation
894        match self {
895            AuthMethodEnum::Password(method) => method.authenticate(credential, metadata).await,
896            AuthMethodEnum::Jwt(method) => {
897                // Extract JWT token from credential
898                let token = match credential {
899                    Credential::Jwt { token } => token,
900                    Credential::Bearer { token } => token,
901                    _ => {
902                        return Err(AuthError::auth_method(
903                            "jwt",
904                            "Invalid credential type for JWT authentication",
905                        ));
906                    }
907                };
908
909                // Authenticate using JWT method
910                match method.authenticate_jwt(&token).await {
911                    Ok(auth_token) => Ok(MethodResult::Success(Box::new(auth_token))),
912                    Err(e) => Ok(MethodResult::Failure {
913                        reason: format!("JWT authentication failed: {}", e),
914                    }),
915                }
916            }
917            AuthMethodEnum::ApiKey(method) => {
918                // Extract API key from credential
919                let api_key = match credential {
920                    Credential::ApiKey { key } => key,
921                    Credential::Bearer { token } => token,
922                    _ => {
923                        return Err(AuthError::auth_method(
924                            "api_key",
925                            "Invalid credential type for API key authentication",
926                        ));
927                    }
928                };
929
930                // Authenticate using API key method
931                match method.authenticate_api_key(&api_key).await {
932                    Ok(auth_token) => Ok(MethodResult::Success(Box::new(auth_token))),
933                    Err(e) => Ok(MethodResult::Failure {
934                        reason: format!("API key authentication failed: {}", e),
935                    }),
936                }
937            }
938            AuthMethodEnum::OAuth2(method) => {
939                // Extract OAuth2 access token from credential
940                let access_token = match credential {
941                    Credential::OAuth {
942                        authorization_code, ..
943                    } => authorization_code,
944                    Credential::Bearer { token } => token,
945                    Credential::OpenIdConnect {
946                        access_token: Some(token),
947                        ..
948                    } => token,
949                    _ => {
950                        return Err(AuthError::auth_method(
951                            "oauth2",
952                            "Invalid credential type for OAuth2 authentication",
953                        ));
954                    }
955                };
956
957                // Authenticate using OAuth2 method
958                match method.authenticate_oauth2(&access_token).await {
959                    Ok(auth_token) => Ok(MethodResult::Success(Box::new(auth_token))),
960                    Err(e) => Ok(MethodResult::Failure {
961                        reason: format!("OAuth2 authentication failed: {}", e),
962                    }),
963                }
964            }
965            #[cfg(feature = "saml")]
966            AuthMethodEnum::Saml(_) => {
967                tracing::warn!("SAML authentication not yet implemented");
968                Ok(MethodResult::Failure {
969                    reason: "SAML authentication not yet implemented".to_string(),
970                })
971            }
972            #[cfg(feature = "ldap-auth")]
973            AuthMethodEnum::Ldap(_) => {
974                tracing::warn!("LDAP authentication not yet implemented");
975                Ok(MethodResult::Failure {
976                    reason: "LDAP authentication not yet implemented".to_string(),
977                })
978            }
979            AuthMethodEnum::HardwareToken(_) => {
980                tracing::warn!("Hardware token authentication not yet implemented");
981                Ok(MethodResult::Failure {
982                    reason: "Hardware token authentication not yet implemented".to_string(),
983                })
984            }
985            AuthMethodEnum::OpenIdConnect(_) => {
986                tracing::warn!("OpenID Connect authentication not yet implemented");
987                Ok(MethodResult::Failure {
988                    reason: "OpenID Connect authentication not yet implemented".to_string(),
989                })
990            }
991            AuthMethodEnum::AdvancedMfa(_) => {
992                tracing::warn!("Advanced MFA authentication not yet implemented");
993                Ok(MethodResult::Failure {
994                    reason: "Advanced MFA authentication not yet implemented".to_string(),
995                })
996            }
997            #[cfg(feature = "enhanced-device-flow")]
998            AuthMethodEnum::EnhancedDeviceFlow(_) => {
999                tracing::warn!("Enhanced device flow authentication not yet implemented");
1000                Ok(MethodResult::Failure {
1001                    reason: "Enhanced device flow authentication not yet implemented".to_string(),
1002                })
1003            }
1004            #[cfg(feature = "passkeys")]
1005            AuthMethodEnum::Passkey(_) => {
1006                tracing::warn!("Passkey authentication not yet implemented");
1007                Ok(MethodResult::Failure {
1008                    reason: "Passkey authentication not yet implemented".to_string(),
1009                })
1010            }
1011        }
1012    }
1013
1014    fn validate_config(&self) -> Result<()> {
1015        // Enhanced stub implementation with basic validation
1016        Ok(())
1017    }
1018
1019    fn supports_refresh(&self) -> bool {
1020        false
1021    }
1022
1023    async fn refresh_token(&self, _refresh_token: String) -> Result<AuthToken, AuthError> {
1024        Err(AuthError::auth_method(
1025            self.name(),
1026            "Token refresh not supported by this method".to_string(),
1027        ))
1028    }
1029}
1030
1031impl MfaChallenge {
1032    /// Create a new MFA challenge.
1033    pub fn new(
1034        mfa_type: MfaType,
1035        user_id: impl Into<String>,
1036        expires_in: std::time::Duration,
1037    ) -> Self {
1038        Self {
1039            id: uuid::Uuid::new_v4().to_string(),
1040            mfa_type,
1041            user_id: user_id.into(),
1042            expires_at: chrono::Utc::now() + chrono::Duration::from_std(expires_in).unwrap(),
1043            message: None,
1044            data: HashMap::new(),
1045        }
1046    }
1047
1048    /// Get the challenge ID.
1049    pub fn id(&self) -> &str {
1050        &self.id
1051    }
1052
1053    /// Check if the challenge has expired.
1054    pub fn is_expired(&self) -> bool {
1055        chrono::Utc::now() > self.expires_at
1056    }
1057
1058    pub fn with_message(mut self, message: impl Into<String>) -> Self {
1059        self.message = Some(message.into());
1060        self
1061    }
1062}