1use crate::{
4 authentication::credentials::{Credential, CredentialMetadata},
5 errors::{AuthError, Result},
6 tokens::AuthToken,
7};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11pub mod enhanced_device;
13pub mod hardware_token;
14pub mod passkey;
15#[cfg(feature = "saml")]
16pub mod saml;
17
18#[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#[derive(Debug, Clone)]
29pub enum MethodResult {
30 Success(Box<AuthToken>),
32
33 MfaRequired(Box<MfaChallenge>),
35
36 Failure { reason: String },
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct MfaChallenge {
43 pub id: String,
45
46 pub mfa_type: MfaType,
48
49 pub user_id: String,
51
52 pub expires_at: chrono::DateTime<chrono::Utc>,
54
55 pub message: Option<String>,
57
58 pub data: HashMap<String, serde_json::Value>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub enum MfaType {
65 Totp,
67
68 Sms { phone_number: String },
70
71 Email { email_address: String },
73
74 Push { device_id: String },
76
77 SecurityKey,
79
80 BackupCode,
82}
83
84pub trait AuthMethod: Send + Sync {
86 type MethodResult: Send + Sync + 'static;
87 type AuthToken: Send + Sync + 'static;
88
89 fn name(&self) -> &str;
91
92 fn authenticate(
94 &self,
95 credential: Credential,
96 metadata: CredentialMetadata,
97 ) -> impl std::future::Future<Output = Result<Self::MethodResult>> + Send;
98
99 fn validate_config(&self) -> Result<()>;
101
102 fn supports_refresh(&self) -> bool {
104 false
105 }
106
107 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#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct UserInfo {
124 pub id: String,
126
127 pub username: String,
129
130 pub email: Option<String>,
132
133 pub name: Option<String>,
135
136 pub roles: Vec<String>,
138
139 pub active: bool,
141
142 pub attributes: HashMap<String, serde_json::Value>,
144}
145
146pub 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#[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 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 async fn authenticate_password(&self, username: &str, password: &str) -> Result<AuthToken> {
193 use crate::utils::password::verify_password;
194
195 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 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 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 let password_hash = user_data["password_hash"]
221 .as_str()
222 .ok_or_else(|| AuthError::internal("Missing password hash".to_string()))?;
223
224 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 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 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(); 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), 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#[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 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 self
336 }
337
338 pub fn issuer(self, _issuer: &str) -> Self {
339 self
341 }
342
343 pub fn audience(self, _audience: &str) -> Self {
344 self
346 }
347
348 async fn authenticate_jwt(&self, token: &str) -> Result<AuthToken> {
350 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 let user_id = jwt_claims.sub.clone();
358
359 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 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#[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 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 async fn authenticate_api_key(&self, api_key: &str) -> Result<AuthToken> {
461 if api_key.len() < 16 {
463 return Err(AuthError::auth_method("api_key", "Invalid API key format"));
464 }
465
466 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 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 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 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 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 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#[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 async fn authenticate_oauth2(&self, access_token: &str) -> Result<AuthToken> {
634 if let Ok(jwt_claims) = self.token_manager.validate_jwt_token(access_token) {
638 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 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 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 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 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
794impl 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 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 tracing::info!(
819 "Password authentication attempt for user: {} from IP: {:?}",
820 username,
821 metadata.client_ip
822 );
823
824 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 Ok(())
850 }
851
852 fn supports_refresh(&self) -> bool {
853 false }
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 match self {
895 AuthMethodEnum::Password(method) => method.authenticate(credential, metadata).await,
896 AuthMethodEnum::Jwt(method) => {
897 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 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 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 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 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 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 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 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 pub fn id(&self) -> &str {
1050 &self.id
1051 }
1052
1053 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}