1pub mod mfa;
55pub mod session_manager;
56pub mod user_manager;
57
58use crate::config::AuthConfig;
59use crate::authentication::credentials::{Credential, CredentialMetadata};
60use crate::errors::{AuthError, MfaError, Result};
61use crate::methods::{AuthMethod, AuthMethodEnum, MethodResult, MfaChallenge};
62use crate::permissions::{Permission, PermissionChecker};
63use crate::storage::{AuthStorage, MemoryStorage};
64use crate::tokens::{AuthToken, TokenManager};
65use crate::utils::rate_limit::RateLimiter;
66use std::collections::HashMap;
67use std::sync::Arc;
68use tokio::sync::RwLock;
69use tracing::{debug, error, info, warn};
70
71pub use mfa::MfaManager;
72pub use session_manager::SessionManager;
73pub use user_manager::{UserInfo, UserManager};
74
75#[derive(Debug, Clone)]
77pub enum AuthResult {
78 Success(Box<AuthToken>),
80
81 MfaRequired(Box<MfaChallenge>),
83
84 Failure(String),
86}
87
88pub struct AuthFramework {
90 config: AuthConfig,
92
93 methods: HashMap<String, AuthMethodEnum>,
95
96 token_manager: TokenManager,
98
99 storage: Arc<dyn AuthStorage>,
101
102 permission_checker: Arc<RwLock<PermissionChecker>>,
104
105 rate_limiter: Option<RateLimiter>,
107
108 mfa_manager: MfaManager,
110
111 session_manager: SessionManager,
113
114 user_manager: UserManager,
116
117 initialized: bool,
119}
120
121impl AuthFramework {
122 pub fn new(config: AuthConfig) -> Self {
124 if let Err(e) = config.validate() {
126 panic!("Invalid configuration: {}", e);
127 }
128
129 let token_manager = if let Some(secret) = &config.security.secret_key {
131 if secret.len() < 32 {
132 eprintln!(
133 "WARNING: JWT secret is shorter than 32 characters. Consider using a longer secret for better security."
134 );
135 }
136 TokenManager::new_hmac(secret.as_bytes(), "auth-framework", "auth-framework")
137 } else if let Some(secret) = &config.secret {
138 if secret.len() < 32 {
139 eprintln!(
140 "WARNING: JWT secret is shorter than 32 characters. Consider using a longer secret for better security."
141 );
142 }
143 TokenManager::new_hmac(secret.as_bytes(), "auth-framework", "auth-framework")
144 } else if let Ok(jwt_secret) = std::env::var("JWT_SECRET") {
145 if jwt_secret.len() < 32 {
146 eprintln!(
147 "WARNING: JWT_SECRET is shorter than 32 characters. Consider using a longer secret for better security."
148 );
149 }
150 TokenManager::new_hmac(jwt_secret.as_bytes(), "auth-framework", "auth-framework")
151 } else {
152 panic!(
153 "JWT secret not set! Please set JWT_SECRET env variable or provide in config.\n\
154 For security reasons, no default secret is provided.\n\
155 Generate a secure secret with: openssl rand -base64 32"
156 );
157 };
158
159 let storage: Arc<dyn AuthStorage> = match &config.storage {
161 #[cfg(feature = "redis-storage")]
162 crate::config::StorageConfig::Redis { url, key_prefix } => Arc::new(
163 crate::storage::RedisStorage::new(url, key_prefix).unwrap_or_else(|e| {
164 panic!("Failed to create Redis storage: {}", e);
165 }),
166 ),
167 _ => Arc::new(MemoryStorage::new()),
168 };
169
170 let rate_limiter = if config.rate_limiting.enabled {
172 Some(RateLimiter::new(
173 config.rate_limiting.max_requests,
174 config.rate_limiting.window,
175 ))
176 } else {
177 None
178 };
179
180 let mfa_manager = MfaManager::new(storage.clone());
182 let session_manager = SessionManager::new(storage.clone());
183 let user_manager = UserManager::new(storage.clone());
184
185 Self {
186 config,
187 methods: HashMap::new(),
188 token_manager,
189 storage,
190 permission_checker: Arc::new(RwLock::new(PermissionChecker::new())),
191 rate_limiter,
192 mfa_manager,
193 session_manager,
194 user_manager,
195 initialized: false,
196 }
197 }
198
199 #[cfg(feature = "smskit")]
201 pub fn new_with_smskit_config(
202 config: AuthConfig,
203 smskit_config: crate::auth_modular::mfa::SmsKitConfig,
204 ) -> Result<Self> {
205 let mut framework = Self::new(config);
207
208 framework.mfa_manager = crate::auth_modular::mfa::MfaManager::new_with_smskit_config(
210 framework.storage.clone(),
211 smskit_config,
212 )?;
213
214 Ok(framework)
215 }
216
217 pub fn register_method(&mut self, name: impl Into<String>, method: AuthMethodEnum) {
219 let name = name.into();
220 info!("Registering authentication method: {}", name);
221
222 if let Err(e) = method.validate_config() {
224 error!("Method '{}' configuration validation failed: {}", name, e);
225 return;
226 }
227
228 self.methods.insert(name, method);
229 }
230
231 pub async fn initialize(&mut self) -> Result<()> {
233 if self.initialized {
234 return Ok(());
235 }
236
237 info!("Initializing authentication framework");
238
239 {
241 let mut checker = self.permission_checker.write().await;
242 checker.create_default_roles();
243 }
244
245 self.cleanup_expired_data().await?;
247
248 self.initialized = true;
249 info!("Authentication framework initialized successfully");
250
251 Ok(())
252 }
253
254 pub async fn authenticate(
256 &self,
257 method_name: &str,
258 credential: Credential,
259 ) -> Result<AuthResult> {
260 self.authenticate_with_metadata(method_name, credential, CredentialMetadata::new())
261 .await
262 }
263
264 pub async fn authenticate_with_metadata(
266 &self,
267 method_name: &str,
268 credential: Credential,
269 metadata: CredentialMetadata,
270 ) -> Result<AuthResult> {
271 use std::time::Instant;
272 use tokio::time::{Duration as TokioDuration, sleep};
273
274 let start_time = Instant::now();
275
276 if !self.initialized {
277 return Err(AuthError::internal("Framework not initialized"));
278 }
279
280 let result = self
282 .authenticate_internal(method_name, credential, metadata)
283 .await;
284
285 let min_duration = TokioDuration::from_millis(100);
287 let elapsed = start_time.elapsed();
288 if elapsed < min_duration {
289 sleep(min_duration - elapsed).await;
290 }
291
292 result
293 }
294
295 async fn authenticate_internal(
297 &self,
298 method_name: &str,
299 credential: Credential,
300 metadata: CredentialMetadata,
301 ) -> Result<AuthResult> {
302 if let Some(ref rate_limiter) = self.rate_limiter {
304 let rate_key = format!(
305 "auth:{}:{}",
306 method_name,
307 metadata.client_ip.as_deref().unwrap_or("unknown")
308 );
309
310 if !rate_limiter.is_allowed(&rate_key) {
311 warn!(
312 "Rate limit exceeded for method '{}' from IP {:?}",
313 method_name, metadata.client_ip
314 );
315 return Err(AuthError::rate_limit("Too many authentication attempts"));
316 }
317 }
318
319 let method = self.methods.get(method_name).ok_or_else(|| {
321 AuthError::auth_method(method_name, "Authentication method not found".to_string())
322 })?;
323
324 debug!(
326 "Authentication attempt with method '{}' for credential: {}",
327 method_name,
328 credential.safe_display()
329 );
330
331 let result = method.authenticate(credential, metadata.clone()).await?;
333
334 match &result {
336 MethodResult::Success(token) => {
337 info!(
338 "Authentication successful for user '{}' with method '{}'",
339 token.user_id, method_name
340 );
341
342 self.storage.store_token(token).await?;
344
345 self.log_audit_event("auth_success", &token.user_id, method_name, &metadata)
347 .await;
348
349 Ok(AuthResult::Success(token.clone()))
350 }
351
352 MethodResult::MfaRequired(challenge) => {
353 info!(
354 "MFA required for user '{}' with method '{}'",
355 challenge.user_id, method_name
356 );
357
358 self.mfa_manager
360 .store_challenge((**challenge).clone())
361 .await?;
362
363 self.log_audit_event("mfa_required", &challenge.user_id, method_name, &metadata)
365 .await;
366
367 Ok(AuthResult::MfaRequired(challenge.clone()))
368 }
369
370 MethodResult::Failure { reason } => {
371 warn!(
372 "Authentication failed for method '{}': {}",
373 method_name, reason
374 );
375
376 self.log_audit_event("auth_failure", "unknown", method_name, &metadata)
378 .await;
379
380 Ok(AuthResult::Failure(reason.clone()))
381 }
382 }
383 }
384
385 pub async fn complete_mfa(&self, challenge: MfaChallenge, mfa_code: &str) -> Result<AuthToken> {
387 debug!("Completing MFA for challenge '{}'", challenge.id);
388
389 let stored_challenge = self
391 .mfa_manager
392 .get_challenge(&challenge.id)
393 .await?
394 .ok_or(MfaError::ChallengeExpired)?;
395
396 if stored_challenge.is_expired() {
397 self.mfa_manager.remove_challenge(&challenge.id).await?;
398 return Err(MfaError::ChallengeExpired.into());
399 }
400
401 let is_valid = match &stored_challenge.mfa_type {
403 crate::methods::MfaType::Totp => {
404 self.mfa_manager
405 .totp
406 .verify_code(&stored_challenge.user_id, mfa_code)
407 .await?
408 }
409 crate::methods::MfaType::Sms { .. } => {
410 self.mfa_manager
411 .sms
412 .verify_code(&challenge.id, mfa_code)
413 .await?
414 }
415 crate::methods::MfaType::Email { .. } => {
416 self.mfa_manager
417 .email
418 .verify_code(&challenge.id, mfa_code)
419 .await?
420 }
421 crate::methods::MfaType::BackupCode => {
422 self.mfa_manager
423 .backup_codes
424 .verify_code(&stored_challenge.user_id, mfa_code)
425 .await?
426 }
427 _ => false,
428 };
429
430 if !is_valid {
431 return Err(MfaError::InvalidCode.into());
432 }
433
434 self.mfa_manager.remove_challenge(&challenge.id).await?;
436
437 let token = self.token_manager.create_auth_token(
439 &challenge.user_id,
440 vec![], "mfa",
442 None,
443 )?;
444
445 self.storage.store_token(&token).await?;
447
448 info!(
449 "MFA completed successfully for user '{}'",
450 challenge.user_id
451 );
452
453 Ok(token)
454 }
455
456 pub async fn validate_token(&self, token: &AuthToken) -> Result<bool> {
458 if !self.initialized {
459 return Err(AuthError::internal("Framework not initialized"));
460 }
461
462 if !token.is_valid() {
464 return Ok(false);
465 }
466
467 self.token_manager.validate_auth_token(token)?;
469
470 if let Some(stored_token) = self.storage.get_token(&token.token_id).await? {
472 let mut updated_token = stored_token;
474 updated_token.mark_used();
475 self.storage.update_token(&updated_token).await?;
476
477 Ok(true)
478 } else {
479 Ok(false)
480 }
481 }
482
483 pub async fn get_user_info(&self, token: &AuthToken) -> Result<UserInfo> {
485 if !self.validate_token(token).await? {
486 return Err(AuthError::auth_method("token", "Invalid token".to_string()));
487 }
488
489 self.user_manager.get_user_info(&token.user_id).await
490 }
491
492 pub async fn check_permission(
494 &self,
495 token: &AuthToken,
496 action: &str,
497 resource: &str,
498 ) -> Result<bool> {
499 if !self.validate_token(token).await? {
500 return Ok(false);
501 }
502
503 let permission = Permission::new(action, resource);
504 let mut checker = self.permission_checker.write().await;
505 checker.check_token_permission(token, &permission)
506 }
507
508 pub fn token_manager(&self) -> &TokenManager {
510 &self.token_manager
511 }
512
513 pub fn mfa_manager(&self) -> &MfaManager {
515 &self.mfa_manager
516 }
517
518 pub fn session_manager(&self) -> &SessionManager {
520 &self.session_manager
521 }
522
523 pub fn user_manager(&self) -> &UserManager {
525 &self.user_manager
526 }
527
528 pub async fn initiate_sms_challenge(&self, user_id: &str) -> Result<String> {
530 self.mfa_manager.sms.initiate_challenge(user_id).await
531 }
532
533 pub async fn send_sms_code(&self, challenge_id: &str, phone_number: &str) -> Result<()> {
535 self.mfa_manager
536 .sms
537 .send_code(challenge_id, phone_number)
538 .await
539 }
540
541 pub async fn generate_sms_code(&self, challenge_id: &str) -> Result<String> {
543 self.mfa_manager.sms.generate_code(challenge_id).await
544 }
545
546 pub async fn verify_sms_code(&self, challenge_id: &str, code: &str) -> Result<bool> {
548 self.mfa_manager.sms.verify_code(challenge_id, code).await
549 }
550
551 pub async fn cleanup_expired_data(&self) -> Result<()> {
553 debug!("Cleaning up expired data");
554
555 self.storage.cleanup_expired().await?;
557
558 self.mfa_manager.cleanup_expired_challenges().await?;
560
561 self.session_manager.cleanup_expired_sessions().await?;
563
564 if let Some(ref rate_limiter) = self.rate_limiter {
566 rate_limiter.cleanup();
567 }
568
569 Ok(())
570 }
571
572 pub async fn get_stats(&self) -> Result<AuthStats> {
574 let mut stats = AuthStats::default();
575
576 for method in self.methods.keys() {
577 stats.registered_methods.push(method.clone());
578 }
579
580 stats.active_mfa_challenges = self.mfa_manager.get_active_challenge_count().await as u64;
581
582 Ok(stats)
583 }
584
585 async fn log_audit_event(
587 &self,
588 event_type: &str,
589 user_id: &str,
590 method: &str,
591 metadata: &CredentialMetadata,
592 ) {
593 if self.config.audit.enabled {
594 let should_log = match event_type {
595 "auth_success" => self.config.audit.log_success,
596 "auth_failure" => self.config.audit.log_failures,
597 "mfa_required" => self.config.audit.log_success,
598 _ => true,
599 };
600
601 if should_log {
602 info!(
603 target: "auth_audit",
604 event_type = event_type,
605 user_id = user_id,
606 method = method,
607 client_ip = metadata.client_ip.as_deref().unwrap_or("unknown"),
608 user_agent = metadata.user_agent.as_deref().unwrap_or("unknown"),
609 timestamp = chrono::Utc::now().to_rfc3339(),
610 "Authentication event"
611 );
612 }
613 }
614 }
615}
616
617#[derive(Debug, Clone, Default)]
619pub struct AuthStats {
620 pub registered_methods: Vec<String>,
622
623 pub active_mfa_challenges: u64,
625
626 pub tokens_issued: u64,
628
629 pub auth_attempts: u64,
631}
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636 use crate::config::{AuthConfig, SecurityConfig};
637 use std::time::Duration;
638
639 #[tokio::test]
640 async fn test_modular_framework_initialization() {
641 let config = AuthConfig::new().security(SecurityConfig {
642 min_password_length: 8,
643 require_password_complexity: false,
644 password_hash_algorithm: crate::config::PasswordHashAlgorithm::Bcrypt,
645 jwt_algorithm: crate::config::JwtAlgorithm::HS256,
646 secret_key: Some("test_secret_key_32_bytes_long!!!!".to_string()),
647 secure_cookies: false,
648 cookie_same_site: crate::config::CookieSameSite::Lax,
649 csrf_protection: false,
650 session_timeout: Duration::from_secs(3600),
651 });
652 let mut framework = AuthFramework::new(config);
653
654 assert!(framework.initialize().await.is_ok());
655 assert!(framework.initialized);
656 }
657
658 #[tokio::test]
659 async fn test_mfa_manager_access() {
660 let config = AuthConfig::new().security(SecurityConfig {
661 min_password_length: 8,
662 require_password_complexity: false,
663 password_hash_algorithm: crate::config::PasswordHashAlgorithm::Bcrypt,
664 jwt_algorithm: crate::config::JwtAlgorithm::HS256,
665 secret_key: Some("test_secret_key_32_bytes_long!!!!".to_string()),
666 secure_cookies: false,
667 cookie_same_site: crate::config::CookieSameSite::Lax,
668 csrf_protection: false,
669 session_timeout: Duration::from_secs(3600),
670 });
671 let framework = AuthFramework::new(config);
672
673 let _mfa_manager = framework.mfa_manager();
675 let _session_manager = framework.session_manager();
676 let _user_manager = framework.user_manager();
677 }
678}
679
680
681