Skip to main content

auth_framework/config/
mod.rs

1//! Configuration types for the authentication framework.
2
3pub mod app_config;
4pub mod config_manager;
5
6// Re-export for easy access
7pub use app_config::{AppConfig, ConfigBuilder as AppConfigBuilder};
8pub use config_manager::{
9    AuthFrameworkSettings, ConfigBuilder as LayeredConfigBuilder, ConfigIntegration, ConfigManager,
10    SessionCookieSettings, SessionSettings,
11};
12
13use crate::errors::{AuthError, Result};
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::time::Duration;
17
18/// Main configuration for the authentication framework.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct AuthConfig {
21    /// Default token lifetime
22    pub token_lifetime: Duration,
23
24    /// Refresh token lifetime
25    pub refresh_token_lifetime: Duration,
26
27    /// Whether multi-factor authentication is enabled
28    pub enable_multi_factor: bool,
29
30    /// JWT issuer for token validation
31    pub issuer: String,
32
33    /// JWT audience for token validation
34    pub audience: String,
35
36    /// JWT secret key (optional - can be set via environment)
37    pub secret: Option<String>,
38
39    /// Storage configuration
40    pub storage: StorageConfig,
41
42    /// Rate limiting configuration
43    pub rate_limiting: RateLimitConfig,
44
45    /// Security configuration
46    pub security: SecurityConfig,
47
48    /// CORS configuration used by all web framework integrations.
49    #[serde(default)]
50    pub cors: CorsConfig,
51
52    /// Audit logging configuration
53    pub audit: AuditConfig,
54
55    /// Whether framework-level caching helpers are enabled.
56    #[serde(default)]
57    pub enable_caching: bool,
58
59    /// Maximum failed authentication attempts before a client should be blocked.
60    #[serde(default = "default_max_failed_attempts")]
61    pub max_failed_attempts: u32,
62
63    /// Whether RBAC helpers are enabled in the configuration model.
64    #[serde(default)]
65    pub enable_rbac: bool,
66
67    /// Whether framework middleware helpers are enabled in the configuration model.
68    #[serde(default)]
69    pub enable_middleware: bool,
70
71    /// Custom settings for different auth methods
72    pub method_configs: HashMap<String, serde_json::Value>,
73
74    /// Force production validation regardless of environment variables.
75    /// Used in tests that explicitly verify production-mode error handling.
76    #[serde(default)]
77    pub force_production_mode: bool,
78}
79
80/// Storage configuration options.
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub enum StorageConfig {
83    /// In-memory storage (not recommended for production)
84    Memory,
85
86    /// Redis storage
87    #[cfg(feature = "redis-storage")]
88    Redis { url: String, key_prefix: String },
89
90    /// PostgreSQL storage
91    #[cfg(feature = "postgres-storage")]
92    Postgres {
93        connection_string: String,
94        table_prefix: String,
95    },
96
97    /// MySQL storage
98    #[cfg(feature = "mysql-storage")]
99    MySQL {
100        connection_string: String,
101        table_prefix: String,
102    },
103
104    /// SQLite storage
105    #[cfg(feature = "sqlite-storage")]
106    Sqlite { connection_string: String },
107
108    /// Custom storage backend
109    Custom(String),
110}
111
112/// Rate limiting configuration.
113///
114/// Controls how many requests each client can make within a sliding time
115/// window.  Use [`RateLimitConfig::new`] for quick construction or
116/// [`Default::default()`] for sensible production defaults (100 req/min).
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct RateLimitConfig {
119    /// Enable rate limiting
120    pub enabled: bool,
121
122    /// Maximum requests per window
123    pub max_requests: u32,
124
125    /// Time window for rate limiting
126    pub window: Duration,
127
128    /// Burst allowance
129    pub burst: u32,
130}
131
132/// Security configuration options.
133///
134/// Governs password policy, JWT signing, cookie flags, CSRF protection,
135/// and session timeouts.  Two named constructors cover the most common
136/// scenarios:
137///
138/// - [`SecurityConfig::secure()`] — hardened production defaults.
139/// - [`SecurityConfig::development()`] — relaxed settings for local work.
140///
141/// For anything in between, start from [`Default::default()`] and
142/// override individual fields.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct SecurityConfig {
145    /// Minimum password length
146    pub min_password_length: usize,
147
148    /// Require password complexity
149    pub require_password_complexity: bool,
150
151    /// Password hash algorithm
152    pub password_hash_algorithm: PasswordHashAlgorithm,
153
154    /// JWT signing algorithm
155    pub jwt_algorithm: JwtAlgorithm,
156
157    /// Secret key for signing (should be loaded from environment)
158    pub secret_key: Option<String>,
159
160    /// Previous secret key to maintain validation capabilities during rotation
161    pub previous_secret_key: Option<String>,
162
163    /// Enable secure cookies
164    pub secure_cookies: bool,
165
166    /// Cookie SameSite policy
167    pub cookie_same_site: CookieSameSite,
168
169    /// CSRF protection
170    pub csrf_protection: bool,
171
172    /// Session timeout
173    pub session_timeout: Duration,
174}
175
176/// Password hashing algorithms.
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub enum PasswordHashAlgorithm {
179    /// Argon2id — recommended for new applications (memory-hard, side-channel resistant).
180    Argon2,
181    /// bcrypt — widely supported, suitable when Argon2 is unavailable.
182    Bcrypt,
183    /// scrypt — memory-hard alternative to bcrypt.
184    Scrypt,
185}
186
187/// JWT signing algorithms.
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub enum JwtAlgorithm {
190    /// HMAC-SHA256 (symmetric).
191    HS256,
192    /// HMAC-SHA384 (symmetric).
193    HS384,
194    /// HMAC-SHA512 (symmetric).
195    HS512,
196    /// RSA-SHA256 (asymmetric) — recommended for multi-service deployments.
197    RS256,
198    /// RSA-SHA384 (asymmetric).
199    RS384,
200    /// RSA-SHA512 (asymmetric).
201    RS512,
202    /// ECDSA-SHA256 (asymmetric) — compact signatures.
203    ES256,
204    /// ECDSA-SHA384 (asymmetric).
205    ES384,
206}
207
208/// Cookie SameSite policies.
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub enum CookieSameSite {
211    /// Cookies sent only in first-party (same-site) requests.
212    Strict,
213    /// Cookies sent in same-site requests and top-level cross-site navigations.
214    Lax,
215    /// Cookies sent in all contexts; requires the `Secure` flag.
216    None,
217}
218
219/// Cross-Origin Resource Sharing (CORS) configuration.
220///
221/// This is the centralized CORS policy used by all web framework integrations
222/// (API server, admin GUI, warp/axum helpers). Individual integrations may
223/// further restrict (but never relax) these settings.
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct CorsConfig {
226    /// Whether CORS headers are emitted at all.
227    pub enabled: bool,
228
229    /// Explicit list of allowed origins, e.g. `["https://app.example.com"]`.
230    ///
231    /// An empty list with `enabled = true` means no cross-origin requests are
232    /// accepted. Never put `"*"` here — use specific origins.
233    pub allowed_origins: Vec<String>,
234
235    /// HTTP methods that cross-origin requests may use.
236    pub allowed_methods: Vec<String>,
237
238    /// HTTP headers that cross-origin requests may include.
239    pub allowed_headers: Vec<String>,
240
241    /// `Access-Control-Max-Age` value in seconds.
242    pub max_age_secs: u32,
243}
244
245impl Default for CorsConfig {
246    fn default() -> Self {
247        Self {
248            enabled: false,
249            allowed_origins: Vec::new(),
250            allowed_methods: vec![
251                "GET".to_string(),
252                "POST".to_string(),
253                "PUT".to_string(),
254                "DELETE".to_string(),
255                "OPTIONS".to_string(),
256            ],
257            allowed_headers: vec![
258                "Authorization".to_string(),
259                "Content-Type".to_string(),
260                "Accept".to_string(),
261            ],
262            max_age_secs: 3600,
263        }
264    }
265}
266
267impl CorsConfig {
268    /// Enable CORS for the given origin(s).
269    ///
270    /// # Example
271    ///
272    /// ```rust
273    /// use auth_framework::config::CorsConfig;
274    ///
275    /// let cors = CorsConfig::for_origins(["https://app.example.com"]);
276    /// assert!(cors.enabled);
277    /// ```
278    pub fn for_origins<I, S>(origins: I) -> Self
279    where
280        I: IntoIterator<Item = S>,
281        S: Into<String>,
282    {
283        Self {
284            enabled: true,
285            allowed_origins: origins.into_iter().map(Into::into).collect(),
286            ..Default::default()
287        }
288    }
289}
290
291/// Audit logging configuration.
292///
293/// Controls which authentication events are recorded and where the
294/// records are stored.  Enabled by default with [`AuditStorage::Tracing`].
295#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct AuditConfig {
297    /// Enable audit logging
298    pub enabled: bool,
299
300    /// Log successful authentications
301    pub log_success: bool,
302
303    /// Log failed authentications
304    pub log_failures: bool,
305
306    /// Log permission checks
307    pub log_permissions: bool,
308
309    /// Log token operations
310    pub log_tokens: bool,
311
312    /// Audit log storage
313    pub storage: AuditStorage,
314}
315
316/// Audit log storage backend.
317///
318/// Pairs with [`AuditConfig`] to specify *where* audit events are persisted.
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub enum AuditStorage {
321    /// Standard logging (via tracing)
322    Tracing,
323
324    /// File-based storage
325    File { path: String },
326
327    /// Database storage
328    Database { connection_string: String },
329
330    /// External service
331    External { endpoint: String, api_key: String },
332}
333
334const fn default_max_failed_attempts() -> u32 {
335    5
336}
337
338impl Default for AuthConfig {
339    fn default() -> Self {
340        Self {
341            token_lifetime: Duration::from_secs(3600), // 1 hour
342            refresh_token_lifetime: Duration::from_secs(86400 * 7), // 7 days
343            enable_multi_factor: false,
344            issuer: "auth-framework".to_string(),
345            audience: "api".to_string(),
346            secret: None,
347            storage: StorageConfig::Memory,
348            rate_limiting: RateLimitConfig::default(),
349            security: SecurityConfig::default(),
350            cors: CorsConfig::default(),
351            audit: AuditConfig::default(),
352            enable_caching: false,
353            max_failed_attempts: default_max_failed_attempts(),
354            enable_rbac: false,
355            enable_middleware: false,
356            method_configs: HashMap::new(),
357            force_production_mode: false,
358        }
359    }
360}
361
362impl Default for RateLimitConfig {
363    fn default() -> Self {
364        Self {
365            enabled: true,
366            max_requests: 100,
367            window: Duration::from_secs(60), // 1 minute
368            burst: 10,
369        }
370    }
371}
372
373impl Default for SecurityConfig {
374    fn default() -> Self {
375        Self {
376            min_password_length: 8,
377            require_password_complexity: true,
378            password_hash_algorithm: PasswordHashAlgorithm::Argon2,
379            jwt_algorithm: JwtAlgorithm::HS256,
380            secret_key: None,
381            previous_secret_key: None,
382            secure_cookies: true,
383            cookie_same_site: CookieSameSite::Lax,
384            csrf_protection: true,
385            session_timeout: Duration::from_secs(3600 * 24), // 24 hours
386        }
387    }
388}
389
390impl Default for AuditConfig {
391    fn default() -> Self {
392        Self {
393            enabled: true,
394            log_success: true,
395            log_failures: true,
396            log_permissions: true,
397            log_tokens: false, // Tokens can be sensitive
398            storage: AuditStorage::Tracing,
399        }
400    }
401}
402
403// ---------------------------------------------------------------------------
404// Display implementations — human-readable summaries for logging / debugging
405// ---------------------------------------------------------------------------
406
407impl std::fmt::Display for AuthConfig {
408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409        write!(
410            f,
411            "AuthConfig {{ tokens: {}s/{}s refresh, storage: {}, mfa: {}, rbac: {}, rate_limit: {}, security: {}, cors: {}, audit: {} }}",
412            self.token_lifetime.as_secs(),
413            self.refresh_token_lifetime.as_secs(),
414            self.storage,
415            if self.enable_multi_factor { "on" } else { "off" },
416            if self.enable_rbac { "on" } else { "off" },
417            self.rate_limiting,
418            self.security,
419            self.cors,
420            self.audit,
421        )
422    }
423}
424
425impl std::fmt::Display for StorageConfig {
426    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427        match self {
428            Self::Memory => write!(f, "memory"),
429            #[cfg(feature = "redis-storage")]
430            Self::Redis { url, .. } => write!(f, "redis({})", url),
431            #[cfg(feature = "postgres-storage")]
432            Self::Postgres { .. } => write!(f, "postgres"),
433            #[cfg(feature = "mysql-storage")]
434            Self::MySQL { .. } => write!(f, "mysql"),
435            #[cfg(feature = "sqlite-storage")]
436            Self::Sqlite { .. } => write!(f, "sqlite"),
437            Self::Custom(name) => write!(f, "custom({})", name),
438        }
439    }
440}
441
442impl std::fmt::Display for SecurityConfig {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        write!(
445            f,
446            "Security {{ pw≥{}, hash: {}, jwt: {}, cookies: {}, csrf: {}, session: {}s }}",
447            self.min_password_length,
448            self.password_hash_algorithm,
449            self.jwt_algorithm,
450            if self.secure_cookies { "secure" } else { "plain" },
451            if self.csrf_protection { "on" } else { "off" },
452            self.session_timeout.as_secs(),
453        )
454    }
455}
456
457impl std::fmt::Display for PasswordHashAlgorithm {
458    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459        match self {
460            Self::Argon2 => write!(f, "argon2id"),
461            Self::Bcrypt => write!(f, "bcrypt"),
462            Self::Scrypt => write!(f, "scrypt"),
463        }
464    }
465}
466
467impl std::fmt::Display for JwtAlgorithm {
468    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469        match self {
470            Self::HS256 => write!(f, "HS256"),
471            Self::HS384 => write!(f, "HS384"),
472            Self::HS512 => write!(f, "HS512"),
473            Self::RS256 => write!(f, "RS256"),
474            Self::RS384 => write!(f, "RS384"),
475            Self::RS512 => write!(f, "RS512"),
476            Self::ES256 => write!(f, "ES256"),
477            Self::ES384 => write!(f, "ES384"),
478        }
479    }
480}
481
482impl std::fmt::Display for RateLimitConfig {
483    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484        if self.enabled {
485            write!(
486                f,
487                "{} req/{}s (burst {})",
488                self.max_requests,
489                self.window.as_secs(),
490                self.burst,
491            )
492        } else {
493            write!(f, "disabled")
494        }
495    }
496}
497
498impl std::fmt::Display for CorsConfig {
499    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500        if self.enabled {
501            if self.allowed_origins.is_empty() {
502                write!(f, "cors(no origins)")
503            } else {
504                write!(f, "cors({})", self.allowed_origins.join(", "))
505            }
506        } else {
507            write!(f, "cors(off)")
508        }
509    }
510}
511
512impl std::fmt::Display for AuditConfig {
513    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
514        if self.enabled {
515            write!(f, "audit({})", self.storage)
516        } else {
517            write!(f, "audit(off)")
518        }
519    }
520}
521
522impl std::fmt::Display for AuditStorage {
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524        match self {
525            Self::Tracing => write!(f, "tracing"),
526            Self::File { path } => write!(f, "file:{}", path),
527            Self::Database { .. } => write!(f, "database"),
528            Self::External { endpoint, .. } => write!(f, "external:{}", endpoint),
529        }
530    }
531}
532
533/// Runtime-mutable configuration subset.
534///
535/// These fields can be updated via the admin API without restarting the server.
536/// Security-sensitive settings (JWT secret, algorithm, storage backend) are
537/// intentionally excluded and require a process restart to change.
538#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct RuntimeConfig {
540    /// Token lifetime in seconds.
541    pub token_lifetime_secs: u64,
542    /// Refresh token lifetime in seconds.
543    pub refresh_token_lifetime_secs: u64,
544    /// Whether MFA is globally enabled.
545    pub enable_multi_factor: bool,
546    /// Whether rate limiting is active.
547    pub rate_limiting_enabled: bool,
548    /// Maximum requests per rate-limit window.
549    pub rate_limit_max_requests: u32,
550    /// Rate-limit window in seconds.
551    pub rate_limit_window_secs: u64,
552    /// Rate-limit burst allowance.
553    pub rate_limit_burst: u32,
554    /// Minimum accepted password length.
555    pub min_password_length: usize,
556    /// Whether password complexity requirements are enforced.
557    pub require_password_complexity: bool,
558    /// Whether the `Secure` flag is set on session cookies.
559    pub secure_cookies: bool,
560    /// Whether CSRF protection middleware is active.
561    pub csrf_protection: bool,
562    /// Session timeout in seconds.
563    pub session_timeout_secs: u64,
564    /// Whether audit logging is active.
565    pub audit_enabled: bool,
566    /// Log successful authentication events.
567    pub audit_log_success: bool,
568    /// Log failed authentication events.
569    pub audit_log_failures: bool,
570    /// Log permission-check events.
571    pub audit_log_permissions: bool,
572    /// Log token issuance/revocation events.
573    pub audit_log_tokens: bool,
574}
575
576impl RuntimeConfig {
577    /// Initialise from a full [`AuthConfig`].
578    ///
579    /// # Example
580    ///
581    /// ```rust
582    /// use auth_framework::config::{AuthConfig, RuntimeConfig};
583    ///
584    /// let auth_cfg = AuthConfig::new();
585    /// let rt = RuntimeConfig::from_auth_config(&auth_cfg);
586    /// assert_eq!(rt.token_lifetime_secs, auth_cfg.token_lifetime.as_secs());
587    /// ```
588    pub fn from_auth_config(cfg: &AuthConfig) -> Self {
589        Self {
590            token_lifetime_secs: cfg.token_lifetime.as_secs(),
591            refresh_token_lifetime_secs: cfg.refresh_token_lifetime.as_secs(),
592            enable_multi_factor: cfg.enable_multi_factor,
593            rate_limiting_enabled: cfg.rate_limiting.enabled,
594            rate_limit_max_requests: cfg.rate_limiting.max_requests,
595            rate_limit_window_secs: cfg.rate_limiting.window.as_secs(),
596            rate_limit_burst: cfg.rate_limiting.burst,
597            min_password_length: cfg.security.min_password_length,
598            require_password_complexity: cfg.security.require_password_complexity,
599            secure_cookies: cfg.security.secure_cookies,
600            csrf_protection: cfg.security.csrf_protection,
601            session_timeout_secs: cfg.security.session_timeout.as_secs(),
602            audit_enabled: cfg.audit.enabled,
603            audit_log_success: cfg.audit.log_success,
604            audit_log_failures: cfg.audit.log_failures,
605            audit_log_permissions: cfg.audit.log_permissions,
606            audit_log_tokens: cfg.audit.log_tokens,
607        }
608    }
609}
610
611impl Default for RuntimeConfig {
612    fn default() -> Self {
613        Self::from_auth_config(&AuthConfig::default())
614    }
615}
616
617impl AuthConfig {
618    /// Create a new configuration with default values.
619    ///
620    /// `AuthConfig` supports two construction styles:
621    ///
622    /// ## Fluent setter chain (simple cases)
623    ///
624    /// ```rust,no_run
625    /// use auth_framework::config::AuthConfig;
626    /// use std::time::Duration;
627    ///
628    /// let config = AuthConfig::new()
629    ///     .token_lifetime(Duration::from_secs(3600))
630    ///     .secret("my-secret-key-at-least-32-chars-long!!");
631    /// ```
632    ///
633    /// ## Full builder (complex / multi-backend setups)
634    ///
635    /// ```rust,no_run
636    /// use auth_framework::prelude::*;
637    ///
638    /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
639    /// let auth = AuthFramework::builder()
640    ///     .with_jwt().secret("...").issuer("myapp").done()
641    ///     .with_storage().memory().done()
642    ///     .security_preset(SecurityPreset::HighSecurity)
643    ///     .build().await?;
644    /// # Ok(()) }
645    /// ```
646    ///
647    /// See [`AuthFramework::builder`] and [`AuthFramework::quick_start`] for
648    /// the full builder APIs.
649    pub fn new() -> Self {
650        Self::default()
651    }
652
653    /// Build a configuration from common environment variables.
654    ///
655    /// Reads the following environment variables (all optional):
656    ///
657    /// | Variable | Maps to |
658    /// |----------|---------|
659    /// | `JWT_SECRET` | `secret` / `security.secret_key` |
660    /// | `DATABASE_URL` | PostgreSQL storage (requires `postgres-storage` feature) |
661    /// | `REDIS_URL` | Redis storage (requires `redis-storage` feature) |
662    /// | `AUTH_ISSUER` | `issuer` |
663    /// | `AUTH_AUDIENCE` | `audience` |
664    ///
665    /// Missing variables are silently ignored and fall back to defaults.
666    ///
667    /// # Example
668    ///
669    /// ```rust
670    /// use auth_framework::config::AuthConfig;
671    ///
672    /// // In tests or CI you can set the env vars beforehand:
673    /// // std::env::set_var("JWT_SECRET", "my-long-secret-key-for-jwt-signing!!");
674    /// let config = AuthConfig::from_env();
675    /// ```
676    pub fn from_env() -> Self {
677        let mut config = Self::default();
678
679        if let Ok(secret) = std::env::var("JWT_SECRET") {
680            config.secret = Some(secret.clone());
681            config.security.secret_key = Some(secret);
682        }
683
684        if let Ok(issuer) = std::env::var("AUTH_ISSUER") {
685            config.issuer = issuer;
686        }
687
688        if let Ok(audience) = std::env::var("AUTH_AUDIENCE") {
689            config.audience = audience;
690        }
691
692        // Storage: DATABASE_URL → Postgres, REDIS_URL → Redis (first match wins)
693        #[cfg(feature = "postgres-storage")]
694        if let Ok(url) = std::env::var("DATABASE_URL") {
695            config.storage = StorageConfig::Postgres {
696                connection_string: url,
697                table_prefix: "auth_".to_string(),
698            };
699        }
700
701        #[cfg(feature = "redis-storage")]
702        if matches!(config.storage, StorageConfig::Memory) {
703            if let Ok(url) = std::env::var("REDIS_URL") {
704                config.storage = StorageConfig::Redis {
705                    url,
706                    key_prefix: "auth:".to_string(),
707                };
708            }
709        }
710
711        config
712    }
713
714    /// Start the full [`AuthBuilder`](crate::builders::AuthBuilder) workflow.
715    ///
716    /// This is a convenience alias for [`AuthFramework::builder()`] — use it
717    /// when you want to configure storage, security presets, and sub-builders
718    /// from a single fluent chain.
719    ///
720    /// # Example
721    ///
722    /// ```rust,no_run
723    /// use auth_framework::prelude::*;
724    ///
725    /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
726    /// let auth = AuthConfig::builder()
727    ///     .with_jwt().secret("...").done()
728    ///     .with_storage().memory().done()
729    ///     .build().await?;
730    /// # Ok(()) }
731    /// ```
732    ///
733    /// For more organized configuration, consider [`AuthConfigBuilder`] which
734    /// groups settings by concern (tokens, security, storage, features, etc.).
735    pub fn builder() -> crate::builders::AuthBuilder {
736        crate::builders::AuthBuilder::new()
737    }
738
739    /// Set the token lifetime.
740    ///
741    /// # Example
742    ///
743    /// ```rust
744    /// use auth_framework::config::AuthConfig;
745    /// use std::time::Duration;
746    ///
747    /// let config = AuthConfig::new().token_lifetime(Duration::from_secs(1800));
748    /// assert_eq!(config.token_lifetime.as_secs(), 1800);
749    /// ```
750    pub fn token_lifetime(mut self, lifetime: Duration) -> Self {
751        self.token_lifetime = lifetime;
752        self
753    }
754
755    /// Set the refresh token lifetime.
756    ///
757    /// # Example
758    ///
759    /// ```rust
760    /// use auth_framework::config::AuthConfig;
761    /// use std::time::Duration;
762    ///
763    /// let config = AuthConfig::new().refresh_token_lifetime(Duration::from_secs(86400));
764    /// assert_eq!(config.refresh_token_lifetime.as_secs(), 86400);
765    /// ```
766    pub fn refresh_token_lifetime(mut self, lifetime: Duration) -> Self {
767        self.refresh_token_lifetime = lifetime;
768        self
769    }
770
771    /// Enable or disable multi-factor authentication.
772    ///
773    /// # Example
774    ///
775    /// ```rust
776    /// use auth_framework::config::AuthConfig;
777    ///
778    /// let config = AuthConfig::new().enable_multi_factor(true);
779    /// assert!(config.enable_multi_factor);
780    /// ```
781    pub fn enable_multi_factor(mut self, enabled: bool) -> Self {
782        self.enable_multi_factor = enabled;
783        self
784    }
785
786    /// Set the JWT issuer.
787    pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
788        self.issuer = issuer.into();
789        self
790    }
791
792    /// Set the JWT audience.
793    pub fn audience(mut self, audience: impl Into<String>) -> Self {
794        self.audience = audience.into();
795        self
796    }
797
798    /// Set the JWT secret key.
799    pub fn secret(mut self, secret: impl Into<String>) -> Self {
800        self.secret = Some(secret.into());
801        self
802    }
803
804    /// Require MFA for all users.
805    ///
806    /// # Example
807    ///
808    /// ```rust
809    /// use auth_framework::config::AuthConfig;
810    ///
811    /// let config = AuthConfig::new().require_mfa(true);
812    /// assert!(config.enable_multi_factor);
813    /// ```
814    pub fn require_mfa(mut self, required: bool) -> Self {
815        self.enable_multi_factor = required;
816        self
817    }
818
819    /// Enable caching.
820    pub fn enable_caching(mut self, enabled: bool) -> Self {
821        self.enable_caching = enabled;
822        self
823    }
824
825    /// Set maximum failed attempts.
826    ///
827    /// # Example
828    ///
829    /// ```rust
830    /// use auth_framework::config::AuthConfig;
831    ///
832    /// let config = AuthConfig::new().max_failed_attempts(10);
833    /// assert_eq!(config.max_failed_attempts, 10);
834    /// ```
835    pub fn max_failed_attempts(mut self, max: u32) -> Self {
836        self.max_failed_attempts = max;
837        self
838    }
839
840    /// Enable RBAC.
841    ///
842    /// # Example
843    ///
844    /// ```rust
845    /// use auth_framework::config::AuthConfig;
846    ///
847    /// let config = AuthConfig::new().enable_rbac(true);
848    /// assert!(config.enable_rbac);
849    /// ```
850    pub fn enable_rbac(mut self, enabled: bool) -> Self {
851        self.enable_rbac = enabled;
852        self
853    }
854
855    /// Enable security audit.
856    ///
857    /// # Example
858    ///
859    /// ```rust
860    /// use auth_framework::config::AuthConfig;
861    ///
862    /// let config = AuthConfig::new().enable_security_audit(true);
863    /// assert!(config.audit.enabled);
864    /// ```
865    pub fn enable_security_audit(mut self, enabled: bool) -> Self {
866        self.audit.enabled = enabled;
867        self
868    }
869
870    /// Enable middleware.
871    ///
872    /// # Example
873    ///
874    /// ```rust
875    /// use auth_framework::config::AuthConfig;
876    ///
877    /// let config = AuthConfig::new().enable_middleware(true);
878    /// assert!(config.enable_middleware);
879    /// ```
880    pub fn enable_middleware(mut self, enabled: bool) -> Self {
881        self.enable_middleware = enabled;
882        self
883    }
884
885    /// Force production-mode validation, bypassing test-environment detection.
886    ///
887    /// Used exclusively in tests that verify production-specific error handling without
888    /// polluting the process-wide environment with `ENVIRONMENT=production`.
889    ///
890    /// # Example
891    ///
892    /// ```rust
893    /// use auth_framework::config::AuthConfig;
894    ///
895    /// let config = AuthConfig::new().force_production_mode();
896    /// ```
897    pub fn force_production_mode(mut self) -> Self {
898        self.force_production_mode = true;
899        self
900    }
901
902    /// Set the storage configuration.
903    ///
904    /// # Example
905    ///
906    /// ```rust
907    /// use auth_framework::config::{AuthConfig, StorageConfig};
908    ///
909    /// let config = AuthConfig::new().storage(StorageConfig::Memory);
910    /// ```
911    pub fn storage(mut self, storage: StorageConfig) -> Self {
912        self.storage = storage;
913        self
914    }
915
916    /// Configure Redis storage.
917    ///
918    /// # Example
919    ///
920    /// ```rust,ignore
921    /// use auth_framework::config::AuthConfig;
922    ///
923    /// let config = AuthConfig::new().redis_storage("redis://127.0.0.1:6379");
924    /// ```
925    #[cfg(feature = "redis-storage")]
926    pub fn redis_storage(mut self, url: impl Into<String>) -> Self {
927        self.storage = StorageConfig::Redis {
928            url: url.into(),
929            key_prefix: "auth:".to_string(),
930        };
931        self
932    }
933
934    /// Set rate limiting configuration.
935    ///
936    /// # Example
937    ///
938    /// ```rust
939    /// use auth_framework::config::{AuthConfig, RateLimitConfig};
940    ///
941    /// let config = AuthConfig::new().rate_limiting(RateLimitConfig::default());
942    /// ```
943    pub fn rate_limiting(mut self, config: RateLimitConfig) -> Self {
944        self.rate_limiting = config;
945        self
946    }
947
948    /// Set security configuration.
949    ///
950    /// # Example
951    ///
952    /// ```rust
953    /// use auth_framework::config::{AuthConfig, SecurityConfig};
954    ///
955    /// let config = AuthConfig::new().security(SecurityConfig::secure());
956    /// ```
957    pub fn security(mut self, config: SecurityConfig) -> Self {
958        self.security = config;
959        self
960    }
961
962    /// Set CORS configuration.
963    ///
964    /// # Example
965    ///
966    /// ```rust
967    /// use auth_framework::config::{AuthConfig, CorsConfig};
968    ///
969    /// let config = AuthConfig::new()
970    ///     .cors(CorsConfig::for_origins(["https://app.example.com"]));
971    /// ```
972    pub fn cors(mut self, config: CorsConfig) -> Self {
973        self.cors = config;
974        self
975    }
976
977    /// Set audit configuration.
978    ///
979    /// # Example
980    ///
981    /// ```rust
982    /// use auth_framework::config::{AuthConfig, AuditConfig};
983    ///
984    /// let config = AuthConfig::new().audit(AuditConfig::default());
985    /// ```
986    pub fn audit(mut self, config: AuditConfig) -> Self {
987        self.audit = config;
988        self
989    }
990
991    /// Add configuration for a specific auth method.
992    ///
993    /// # Example
994    ///
995    /// ```rust
996    /// use auth_framework::config::AuthConfig;
997    ///
998    /// let config = AuthConfig::new()
999    ///     .method_config("oauth2", serde_json::json!({
1000    ///         "client_id": "my-client",
1001    ///         "client_secret": "my-secret"
1002    ///     }))
1003    ///     .unwrap();
1004    /// ```
1005    pub fn method_config(
1006        mut self,
1007        method_name: impl Into<String>,
1008        config: impl Serialize,
1009    ) -> Result<Self> {
1010        let value = serde_json::to_value(config)
1011            .map_err(|e| AuthError::config(format!("Failed to serialize method config: {e}")))?;
1012
1013        self.method_configs.insert(method_name.into(), value);
1014        Ok(self)
1015    }
1016
1017    /// Get configuration for a specific auth method.
1018    ///
1019    /// # Example
1020    ///
1021    /// ```rust
1022    /// use auth_framework::config::AuthConfig;
1023    ///
1024    /// let config = AuthConfig::new();
1025    /// let oauth: Option<serde_json::Value> = config.get_method_config("oauth2").unwrap();
1026    /// assert!(oauth.is_none()); // no oauth2 config set yet
1027    /// ```
1028    pub fn get_method_config<T>(&self, method_name: &str) -> Result<Option<T>>
1029    where
1030        T: for<'de> Deserialize<'de>,
1031    {
1032        if let Some(value) = self.method_configs.get(method_name) {
1033            let config = serde_json::from_value(value.clone()).map_err(|e| {
1034                AuthError::config(format!("Failed to deserialize method config: {e}"))
1035            })?;
1036            Ok(Some(config))
1037        } else {
1038            Ok(None)
1039        }
1040    }
1041
1042    /// Validate the configuration.
1043    ///
1044    /// # Example
1045    ///
1046    /// ```rust
1047    /// use auth_framework::config::AuthConfig;
1048    ///
1049    /// let config = AuthConfig::new();
1050    /// assert!(config.validate().is_ok());
1051    /// ```
1052    pub fn validate(&self) -> Result<()> {
1053        // Validate token lifetimes
1054        if self.token_lifetime.as_secs() == 0 {
1055            return Err(AuthError::config("Token lifetime must be greater than 0"));
1056        }
1057
1058        if self.refresh_token_lifetime.as_secs() == 0 {
1059            return Err(AuthError::config(
1060                "Refresh token lifetime must be greater than 0",
1061            ));
1062        }
1063
1064        if self.refresh_token_lifetime <= self.token_lifetime {
1065            return Err(AuthError::config(
1066                "Refresh token lifetime must be greater than token lifetime",
1067            ));
1068        }
1069
1070        // Validate JWT secret configuration
1071        self.validate_jwt_secret()?;
1072
1073        // Validate security settings
1074        if self.security.min_password_length < 4 {
1075            return Err(AuthError::config(
1076                "Minimum password length must be at least 4 characters",
1077            ));
1078        }
1079
1080        // Enhanced security validation for production
1081        if self.is_production_environment() && !self.is_test_environment() {
1082            self.validate_production_security()?;
1083        }
1084
1085        // Validate rate limiting
1086        if self.rate_limiting.enabled && self.rate_limiting.max_requests == 0 {
1087            return Err(AuthError::config(
1088                "Rate limit max requests must be greater than 0 when enabled",
1089            ));
1090        }
1091
1092        // Validate storage configuration
1093        self.validate_storage_config()?;
1094
1095        // Validate built-in auth method configuration blobs eagerly so invalid
1096        // method settings fail during startup instead of at first use.
1097        self.validate_method_configs()?;
1098
1099        Ok(())
1100    }
1101
1102    fn validate_method_configs(&self) -> Result<()> {
1103        for (method_name, raw_config) in &self.method_configs {
1104            match method_name.as_str() {
1105                #[cfg(feature = "saml")]
1106                "saml" => {
1107                    let config: crate::methods::saml::SamlConfig =
1108                        serde_json::from_value(raw_config.clone()).map_err(|e| {
1109                            AuthError::config(format!(
1110                                "Failed to deserialize SAML method config: {e}"
1111                            ))
1112                        })?;
1113
1114                    if config.entity_id.trim().is_empty() {
1115                        return Err(AuthError::config("SAML entity_id cannot be empty"));
1116                    }
1117                    if config.acs_url.trim().is_empty() {
1118                        return Err(AuthError::config("SAML acs_url cannot be empty"));
1119                    }
1120                    if config.max_assertion_age == 0 {
1121                        return Err(AuthError::config(
1122                            "SAML max_assertion_age must be greater than 0",
1123                        ));
1124                    }
1125                }
1126                #[cfg(not(feature = "saml"))]
1127                "saml" => {
1128                    return Err(AuthError::config(
1129                        "SAML method config is present but the 'saml' feature is not enabled",
1130                    ));
1131                }
1132                #[cfg(feature = "passkeys")]
1133                "passkey" => {
1134                    let config: crate::methods::passkey::PasskeyConfig =
1135                        serde_json::from_value(raw_config.clone()).map_err(|e| {
1136                            AuthError::config(format!(
1137                                "Failed to deserialize passkey method config: {e}"
1138                            ))
1139                        })?;
1140
1141                    if config.rp_id.trim().is_empty() {
1142                        return Err(AuthError::config("Passkey RP ID cannot be empty"));
1143                    }
1144                    if config.origin.trim().is_empty() {
1145                        return Err(AuthError::config("Passkey origin cannot be empty"));
1146                    }
1147                    if config.timeout_ms == 0 {
1148                        return Err(AuthError::config("Passkey timeout must be greater than 0"));
1149                    }
1150                    match config.user_verification.as_str() {
1151                        "required" | "preferred" | "discouraged" => {}
1152                        _ => {
1153                            return Err(AuthError::config(
1154                                "Invalid passkey user_verification value",
1155                            ));
1156                        }
1157                    }
1158                    url::Url::parse(&config.origin).map_err(|e| {
1159                        AuthError::config(format!("Invalid passkey origin URL: {e}"))
1160                    })?;
1161                }
1162                #[cfg(not(feature = "passkeys"))]
1163                "passkey" => {
1164                    return Err(AuthError::config(
1165                        "Passkey method config is present but the 'passkeys' feature is not enabled",
1166                    ));
1167                }
1168                "enhanced_device_flow" => {
1169                    let client_id = raw_config
1170                        .get("client_id")
1171                        .and_then(serde_json::Value::as_str)
1172                        .unwrap_or_default();
1173                    let auth_url = raw_config
1174                        .get("auth_url")
1175                        .and_then(serde_json::Value::as_str)
1176                        .unwrap_or_default();
1177                    let token_url = raw_config
1178                        .get("token_url")
1179                        .and_then(serde_json::Value::as_str)
1180                        .unwrap_or_default();
1181                    let device_auth_url = raw_config
1182                        .get("device_auth_url")
1183                        .and_then(serde_json::Value::as_str)
1184                        .unwrap_or_default();
1185
1186                    if client_id.trim().is_empty()
1187                        || auth_url.trim().is_empty()
1188                        || token_url.trim().is_empty()
1189                        || device_auth_url.trim().is_empty()
1190                    {
1191                        return Err(AuthError::config(
1192                            "Enhanced device flow config requires non-empty client_id, auth_url, token_url, and device_auth_url",
1193                        ));
1194                    }
1195                }
1196                _ => {}
1197            }
1198        }
1199
1200        Ok(())
1201    }
1202
1203    /// Validate JWT secret configuration for security
1204    fn validate_jwt_secret(&self) -> Result<()> {
1205        // Check multiple sources for JWT secret
1206        let env_secret = std::env::var("JWT_SECRET").ok();
1207        let jwt_secret = self
1208            .security
1209            .secret_key
1210            .as_ref()
1211            .or(self.secret.as_ref())
1212            .or(env_secret.as_ref());
1213
1214        if let Some(secret) = jwt_secret {
1215            // Enforce minimum length only outside test environments.
1216            // In tests, short secrets are acceptable for convenience.
1217            if !self.is_test_environment() && secret.len() < 32 {
1218                return Err(AuthError::config(
1219                    "JWT secret must be at least 32 characters for security. \
1220                     Generate with: openssl rand -base64 32",
1221                ));
1222            }
1223
1224            // Check for low-entropy secrets using Shannon entropy measurement
1225            // rather than pattern matching, which is trivially bypassed.
1226            if !self.is_test_environment() {
1227                let mut char_counts = std::collections::HashMap::new();
1228                for c in secret.chars() {
1229                    *char_counts.entry(c).or_insert(0u32) += 1;
1230                }
1231                let len = secret.len() as f64;
1232                let entropy: f64 = char_counts
1233                    .values()
1234                    .map(|&count| {
1235                        let p = count as f64 / len;
1236                        -p * p.log2()
1237                    })
1238                    .sum();
1239                if entropy < 3.5 {
1240                    return Err(AuthError::config(
1241                        "JWT secret has insufficient entropy (too predictable). \
1242                         Use a cryptographically secure random string.",
1243                    ));
1244                }
1245            }
1246
1247            // Warn if secret looks like it might be base64 but too short
1248            if secret.len() < 44
1249                && secret
1250                    .chars()
1251                    .all(|c| c.is_alphanumeric() || c == '+' || c == '/' || c == '=')
1252            {
1253                tracing::warn!(
1254                    "JWT secret may be too short for optimal security. \
1255                     Consider using at least 44 characters (32 bytes base64-encoded)."
1256                );
1257            }
1258        } else if self.is_production_environment() {
1259            return Err(AuthError::config(
1260                "JWT secret is required for production environments. \
1261                 Set JWT_SECRET environment variable or configure security.secret_key",
1262            ));
1263        }
1264
1265        Ok(())
1266    }
1267
1268    /// Validate production-specific security requirements
1269    fn validate_production_security(&self) -> Result<()> {
1270        // Require strong password policies in production
1271        if self.security.min_password_length < 8 {
1272            return Err(AuthError::config(
1273                "Production environments require minimum password length of 8 characters",
1274            ));
1275        }
1276
1277        if !self.security.require_password_complexity {
1278            tracing::warn!("Production deployment should enable password complexity requirements");
1279        }
1280
1281        // Require secure cookies in production
1282        if !self.security.secure_cookies {
1283            return Err(AuthError::config(
1284                "Production environments must use secure cookies (HTTPS required)",
1285            ));
1286        }
1287
1288        // Ensure rate limiting is enabled
1289        if !self.rate_limiting.enabled {
1290            tracing::warn!("Production deployment should enable rate limiting for security");
1291        }
1292
1293        // Validate audit configuration for compliance
1294        if !self.audit.enabled {
1295            return Err(AuthError::config(
1296                "Production environments require audit logging for compliance",
1297            ));
1298        }
1299
1300        Ok(())
1301    }
1302
1303    /// Validate storage configuration
1304    fn validate_storage_config(&self) -> Result<()> {
1305        match &self.storage {
1306            StorageConfig::Memory => {
1307                if self.is_production_environment() && !self.is_test_environment() {
1308                    return Err(AuthError::config(
1309                        "Memory storage is not suitable for production environments. \
1310                         Use PostgreSQL, Redis, MySQL, or SQLite storage.",
1311                    ));
1312                }
1313            }
1314            #[cfg(feature = "mysql-storage")]
1315            StorageConfig::MySQL { .. } => {
1316                tracing::warn!(
1317                    "MySQL storage has known RSA vulnerability (RUSTSEC-2023-0071). \
1318                     Consider using PostgreSQL for enhanced security."
1319                );
1320            }
1321            _ => {} // PostgreSQL and Redis are production-ready
1322        }
1323
1324        Ok(())
1325    }
1326
1327    /// Detect production environment
1328    fn is_production_environment(&self) -> bool {
1329        // An explicit config flag always wins — used by tests that need production behaviour.
1330        if self.force_production_mode {
1331            return true;
1332        }
1333
1334        // Check common production environment indicators
1335        if let Ok(env) = std::env::var("ENVIRONMENT")
1336            && (env.to_lowercase() == "production" || env.to_lowercase() == "prod")
1337        {
1338            return true;
1339        }
1340
1341        if let Ok(env) = std::env::var("ENV")
1342            && (env.to_lowercase() == "production" || env.to_lowercase() == "prod")
1343        {
1344            return true;
1345        }
1346
1347        if let Ok(env) = std::env::var("NODE_ENV")
1348            && env.to_lowercase() == "production"
1349        {
1350            return true;
1351        }
1352
1353        if let Ok(env) = std::env::var("RUST_ENV")
1354            && env.to_lowercase() == "production"
1355        {
1356            return true;
1357        }
1358
1359        // Check for containerized environments
1360        if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
1361            return true;
1362        }
1363
1364        if std::env::var("DOCKER_CONTAINER").is_ok() {
1365            return true;
1366        }
1367
1368        false
1369    }
1370
1371    /// Detect test environment
1372    fn is_test_environment(&self) -> bool {
1373        // An explicit config flag forces production mode — never treat it as a test environment.
1374        if self.force_production_mode {
1375            return false;
1376        }
1377        // Check if we're running in a test environment
1378        cfg!(test)
1379            || std::thread::current()
1380                .name()
1381                .is_some_and(|name| name.contains("test"))
1382            || std::env::var("RUST_TEST").is_ok()
1383            || std::env::var("ENVIRONMENT").as_deref() == Ok("test")
1384            || std::env::var("ENV").as_deref() == Ok("test")
1385            || std::env::args().any(|arg| arg.contains("test"))
1386    }
1387}
1388
1389impl RateLimitConfig {
1390    /// Create a new rate limit configuration.
1391    ///
1392    /// # Example
1393    ///
1394    /// ```rust
1395    /// use auth_framework::config::RateLimitConfig;
1396    /// use std::time::Duration;
1397    ///
1398    /// let rl = RateLimitConfig::new(100, Duration::from_secs(60));
1399    /// assert!(rl.enabled);
1400    /// assert_eq!(rl.max_requests, 100);
1401    /// ```
1402    pub fn new(max_requests: u32, window: Duration) -> Self {
1403        Self {
1404            enabled: true,
1405            max_requests,
1406            window,
1407            burst: max_requests / 10, // 10% of max as burst
1408        }
1409    }
1410
1411    /// Disable rate limiting.
1412    ///
1413    /// # Example
1414    ///
1415    /// ```rust
1416    /// use auth_framework::config::RateLimitConfig;
1417    ///
1418    /// let rl = RateLimitConfig::disabled();
1419    /// assert!(!rl.enabled);
1420    /// ```
1421    pub fn disabled() -> Self {
1422        Self {
1423            enabled: false,
1424            ..Default::default()
1425        }
1426    }
1427
1428    /// Shorthand: allow `max` requests per second.
1429    ///
1430    /// # Example
1431    ///
1432    /// ```rust
1433    /// use auth_framework::config::RateLimitConfig;
1434    ///
1435    /// let rl = RateLimitConfig::per_second(50);
1436    /// assert_eq!(rl.max_requests, 50);
1437    /// ```
1438    pub fn per_second(max: u32) -> Self {
1439        Self::new(max, Duration::from_secs(1))
1440    }
1441
1442    /// Shorthand: allow `max` requests per minute.
1443    ///
1444    /// # Example
1445    ///
1446    /// ```rust
1447    /// use auth_framework::config::RateLimitConfig;
1448    ///
1449    /// let rl = RateLimitConfig::per_minute(100);
1450    /// assert_eq!(rl.max_requests, 100);
1451    /// ```
1452    pub fn per_minute(max: u32) -> Self {
1453        Self::new(max, Duration::from_secs(60))
1454    }
1455
1456    /// Shorthand: allow `max` requests per hour.
1457    ///
1458    /// # Example
1459    ///
1460    /// ```rust
1461    /// use auth_framework::config::RateLimitConfig;
1462    ///
1463    /// let rl = RateLimitConfig::per_hour(1000);
1464    /// assert_eq!(rl.max_requests, 1000);
1465    /// ```
1466    pub fn per_hour(max: u32) -> Self {
1467        Self::new(max, Duration::from_secs(3600))
1468    }
1469}
1470
1471impl SecurityConfig {
1472    /// Create a new security configuration with secure defaults.
1473    ///
1474    /// # Example
1475    ///
1476    /// ```rust
1477    /// use auth_framework::config::SecurityConfig;
1478    ///
1479    /// let sec = SecurityConfig::secure();
1480    /// assert!(sec.secure_cookies);
1481    /// assert!(sec.csrf_protection);
1482    /// assert_eq!(sec.min_password_length, 12);
1483    /// ```
1484    pub fn secure() -> Self {
1485        Self {
1486            min_password_length: 12,
1487            require_password_complexity: true,
1488            password_hash_algorithm: PasswordHashAlgorithm::Argon2,
1489            jwt_algorithm: JwtAlgorithm::RS256,
1490            secret_key: None,
1491            previous_secret_key: None,
1492            secure_cookies: true,
1493            cookie_same_site: CookieSameSite::Strict,
1494            csrf_protection: true,
1495            session_timeout: Duration::from_secs(3600 * 8), // 8 hours
1496        }
1497    }
1498
1499    /// Create a development-friendly configuration.
1500    /// WARNING: You MUST set a secret key before using this configuration!
1501    /// Use either config.security.secret_key or JWT_SECRET environment variable.
1502    ///
1503    /// # Example
1504    ///
1505    /// ```rust
1506    /// use auth_framework::config::SecurityConfig;
1507    ///
1508    /// let sec = SecurityConfig::development();
1509    /// assert_eq!(sec.min_password_length, 6);
1510    /// assert!(!sec.secure_cookies);
1511    /// ```
1512    pub fn development() -> Self {
1513        Self {
1514            min_password_length: 6,
1515            require_password_complexity: false,
1516            password_hash_algorithm: PasswordHashAlgorithm::Bcrypt,
1517            jwt_algorithm: JwtAlgorithm::HS256,
1518            secret_key: None, // Must be set by developer for security
1519            previous_secret_key: None,
1520            secure_cookies: false,
1521            cookie_same_site: CookieSameSite::Lax,
1522            csrf_protection: false,
1523            session_timeout: Duration::from_secs(3600 * 24), // 24 hours
1524        }
1525    }
1526}
1527
1528/// A comprehensive builder for `AuthConfig` that organizes configuration into logical groups.
1529///
1530/// This builder provides better developer experience by grouping related settings
1531/// and providing sensible defaults for each category. Use this when you need fine-grained
1532/// control over the configuration but want better organization than the flat setter API.
1533///
1534/// # Example
1535///
1536/// ```rust,no_run
1537/// use auth_framework::config::{AuthConfig, AuthConfigBuilder};
1538/// use std::time::Duration;
1539///
1540/// let builder = AuthConfigBuilder::new()
1541///     .tokens()
1542///         .lifetime(Duration::from_secs(3600))
1543///         .refresh_lifetime(Duration::from_secs(86400 * 7))
1544///         .issuer("myapp")
1545///         .audience("myapp-users")
1546///         .secret("my-32-char-secret-key-here!!!!!")
1547///         .done()
1548///     .security()
1549///         .min_password_length(8)
1550///         .require_complexity(true)
1551///         .secure_cookies(true)
1552///         .done()
1553///     .storage()
1554///         .memory()
1555///         .done()
1556///     .features()
1557///         .enable_multi_factor(true)
1558///         .enable_rbac(true)
1559///         .enable_caching(true)
1560///         .done();
1561///
1562/// let config = builder.build().expect("Failed to build config");
1563/// ```
1564#[derive(Debug)]
1565pub struct AuthConfigBuilder {
1566    config: AuthConfig,
1567}
1568
1569impl Default for AuthConfigBuilder {
1570    fn default() -> Self {
1571        Self::new()
1572    }
1573}
1574
1575impl AuthConfigBuilder {
1576    /// Create a new builder with sensible defaults.
1577    pub fn new() -> Self {
1578        Self {
1579            config: AuthConfig::default(),
1580        }
1581    }
1582
1583    /// Configure token-related settings.
1584    pub fn tokens(self) -> TokenConfigBuilder {
1585        TokenConfigBuilder { builder: self }
1586    }
1587
1588    /// Configure security-related settings.
1589    pub fn security(self) -> SecurityConfigBuilder {
1590        SecurityConfigBuilder { builder: self }
1591    }
1592
1593    /// Configure storage settings.
1594    pub fn storage(self) -> StorageConfigBuilder {
1595        StorageConfigBuilder { builder: self }
1596    }
1597
1598    /// Configure feature flags and capabilities.
1599    pub fn features(self) -> FeatureConfigBuilder {
1600        FeatureConfigBuilder { builder: self }
1601    }
1602
1603    /// Configure rate limiting.
1604    pub fn rate_limiting(self) -> RateLimitConfigBuilder {
1605        RateLimitConfigBuilder { builder: self }
1606    }
1607
1608    /// Configure CORS settings.
1609    pub fn cors(self) -> CorsConfigBuilder {
1610        CorsConfigBuilder { builder: self }
1611    }
1612
1613    /// Configure audit logging.
1614    pub fn audit(self) -> AuditConfigBuilder {
1615        AuditConfigBuilder { builder: self }
1616    }
1617
1618    /// Build the final configuration, validating it in the process.
1619    pub fn build(self) -> Result<AuthConfig> {
1620        let config = self.config;
1621        config.validate()?;
1622        Ok(config)
1623    }
1624}
1625
1626/// Builder for token-related configuration.
1627#[derive(Debug)]
1628pub struct TokenConfigBuilder {
1629    builder: AuthConfigBuilder,
1630}
1631
1632impl TokenConfigBuilder {
1633    /// Set the access token lifetime.
1634    pub fn lifetime(mut self, duration: Duration) -> Self {
1635        self.builder.config.token_lifetime = duration;
1636        self
1637    }
1638
1639    /// Set the refresh token lifetime.
1640    pub fn refresh_lifetime(mut self, duration: Duration) -> Self {
1641        self.builder.config.refresh_token_lifetime = duration;
1642        self
1643    }
1644
1645    /// Set the JWT issuer.
1646    pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
1647        self.builder.config.issuer = issuer.into();
1648        self
1649    }
1650
1651    /// Set the JWT audience.
1652    pub fn audience(mut self, audience: impl Into<String>) -> Self {
1653        self.builder.config.audience = audience.into();
1654        self
1655    }
1656
1657    /// Set the JWT secret key.
1658    pub fn secret(mut self, secret: impl Into<String>) -> Self {
1659        self.builder.config.secret = Some(secret.into());
1660        self
1661    }
1662
1663    /// Finish token configuration and return to the main builder.
1664    pub fn done(self) -> AuthConfigBuilder {
1665        self.builder
1666    }
1667}
1668
1669/// Builder for security-related configuration.
1670#[derive(Debug)]
1671pub struct SecurityConfigBuilder {
1672    builder: AuthConfigBuilder,
1673}
1674
1675impl SecurityConfigBuilder {
1676    /// Set minimum password length.
1677    pub fn min_password_length(mut self, length: usize) -> Self {
1678        self.builder.config.security.min_password_length = length;
1679        self
1680    }
1681
1682    /// Require password complexity.
1683    pub fn require_complexity(mut self, required: bool) -> Self {
1684        self.builder.config.security.require_password_complexity = required;
1685        self
1686    }
1687
1688    /// Set password hash algorithm.
1689    pub fn password_algorithm(mut self, algorithm: PasswordHashAlgorithm) -> Self {
1690        self.builder.config.security.password_hash_algorithm = algorithm;
1691        self
1692    }
1693
1694    /// Set JWT algorithm.
1695    pub fn jwt_algorithm(mut self, algorithm: JwtAlgorithm) -> Self {
1696        self.builder.config.security.jwt_algorithm = algorithm;
1697        self
1698    }
1699
1700    /// Enable secure cookies.
1701    pub fn secure_cookies(mut self, enabled: bool) -> Self {
1702        self.builder.config.security.secure_cookies = enabled;
1703        self
1704    }
1705
1706    /// Set cookie SameSite policy.
1707    pub fn cookie_same_site(mut self, policy: CookieSameSite) -> Self {
1708        self.builder.config.security.cookie_same_site = policy;
1709        self
1710    }
1711
1712    /// Enable CSRF protection.
1713    pub fn csrf_protection(mut self, enabled: bool) -> Self {
1714        self.builder.config.security.csrf_protection = enabled;
1715        self
1716    }
1717
1718    /// Set session timeout.
1719    pub fn session_timeout(mut self, timeout: Duration) -> Self {
1720        self.builder.config.security.session_timeout = timeout;
1721        self
1722    }
1723
1724    /// Finish security configuration and return to the main builder.
1725    pub fn done(self) -> AuthConfigBuilder {
1726        self.builder
1727    }
1728}
1729
1730/// Builder for storage configuration.
1731#[derive(Debug)]
1732pub struct StorageConfigBuilder {
1733    builder: AuthConfigBuilder,
1734}
1735
1736impl StorageConfigBuilder {
1737    /// Use in-memory storage (development only).
1738    pub fn memory(mut self) -> Self {
1739        self.builder.config.storage = StorageConfig::Memory;
1740        self
1741    }
1742
1743    /// Use Redis storage.
1744    #[cfg(feature = "redis-storage")]
1745    pub fn redis(mut self, url: impl Into<String>) -> Self {
1746        self.builder.config.storage = StorageConfig::Redis {
1747            url: url.into(),
1748            key_prefix: "auth:".to_string(),
1749        };
1750        self
1751    }
1752
1753    /// Use PostgreSQL storage.
1754    #[cfg(feature = "postgres-storage")]
1755    pub fn postgres(mut self, connection_string: impl Into<String>) -> Self {
1756        self.builder.config.storage = StorageConfig::Postgres {
1757            connection_string: connection_string.into(),
1758            table_prefix: "auth_".to_string(),
1759        };
1760        self
1761    }
1762
1763    /// Use MySQL storage.
1764    #[cfg(feature = "mysql-storage")]
1765    pub fn mysql(mut self, connection_string: impl Into<String>) -> Self {
1766        self.builder.config.storage = StorageConfig::MySQL {
1767            connection_string: connection_string.into(),
1768            table_prefix: "auth_".to_string(),
1769        };
1770        self
1771    }
1772
1773    /// Use SQLite storage.
1774    #[cfg(feature = "sqlite-storage")]
1775    pub fn sqlite(mut self, connection_string: impl Into<String>) -> Self {
1776        self.builder.config.storage = StorageConfig::Sqlite {
1777            connection_string: connection_string.into(),
1778        };
1779        self
1780    }
1781
1782    /// Finish storage configuration and return to the main builder.
1783    pub fn done(self) -> AuthConfigBuilder {
1784        self.builder
1785    }
1786}
1787
1788/// Builder for feature flags and capabilities.
1789#[derive(Debug)]
1790pub struct FeatureConfigBuilder {
1791    builder: AuthConfigBuilder,
1792}
1793
1794impl FeatureConfigBuilder {
1795    /// Enable multi-factor authentication.
1796    pub fn enable_multi_factor(mut self, enabled: bool) -> Self {
1797        self.builder.config.enable_multi_factor = enabled;
1798        self
1799    }
1800
1801    /// Enable RBAC (Role-Based Access Control).
1802    pub fn enable_rbac(mut self, enabled: bool) -> Self {
1803        self.builder.config.enable_rbac = enabled;
1804        self
1805    }
1806
1807    /// Enable caching.
1808    pub fn enable_caching(mut self, enabled: bool) -> Self {
1809        self.builder.config.enable_caching = enabled;
1810        self
1811    }
1812
1813    /// Enable middleware helpers.
1814    pub fn enable_middleware(mut self, enabled: bool) -> Self {
1815        self.builder.config.enable_middleware = enabled;
1816        self
1817    }
1818
1819    /// Set maximum failed authentication attempts.
1820    pub fn max_failed_attempts(mut self, max: u32) -> Self {
1821        self.builder.config.max_failed_attempts = max;
1822        self
1823    }
1824
1825    /// Finish feature configuration and return to the main builder.
1826    pub fn done(self) -> AuthConfigBuilder {
1827        self.builder
1828    }
1829}
1830
1831/// Builder for rate limiting configuration.
1832#[derive(Debug)]
1833pub struct RateLimitConfigBuilder {
1834    builder: AuthConfigBuilder,
1835}
1836
1837impl RateLimitConfigBuilder {
1838    /// Enable rate limiting with specified limits.
1839    pub fn enabled(mut self, max_requests: u32, window: Duration) -> Self {
1840        self.builder.config.rate_limiting = RateLimitConfig::new(max_requests, window);
1841        self
1842    }
1843
1844    /// Disable rate limiting.
1845    pub fn disabled(mut self) -> Self {
1846        self.builder.config.rate_limiting = RateLimitConfig::disabled();
1847        self
1848    }
1849
1850    /// Set maximum requests per window.
1851    pub fn max_requests(mut self, max: u32) -> Self {
1852        self.builder.config.rate_limiting.max_requests = max;
1853        self
1854    }
1855
1856    /// Set rate limiting window.
1857    pub fn window(mut self, window: Duration) -> Self {
1858        self.builder.config.rate_limiting.window = window;
1859        self
1860    }
1861
1862    /// Set burst allowance.
1863    pub fn burst(mut self, burst: u32) -> Self {
1864        self.builder.config.rate_limiting.burst = burst;
1865        self
1866    }
1867
1868    /// Finish rate limiting configuration and return to the main builder.
1869    pub fn done(self) -> AuthConfigBuilder {
1870        self.builder
1871    }
1872}
1873
1874/// Builder for CORS configuration.
1875#[derive(Debug)]
1876pub struct CorsConfigBuilder {
1877    builder: AuthConfigBuilder,
1878}
1879
1880impl CorsConfigBuilder {
1881    /// Enable CORS for specific origins.
1882    pub fn allow_origins<I, S>(mut self, origins: I) -> Self
1883    where
1884        I: IntoIterator<Item = S>,
1885        S: Into<String>,
1886    {
1887        self.builder.config.cors = CorsConfig::for_origins(origins);
1888        self
1889    }
1890
1891    /// Disable CORS.
1892    pub fn disabled(mut self) -> Self {
1893        self.builder.config.cors.enabled = false;
1894        self
1895    }
1896
1897    /// Allow specific HTTP methods.
1898    pub fn allow_methods<I, S>(mut self, methods: I) -> Self
1899    where
1900        I: IntoIterator<Item = S>,
1901        S: Into<String>,
1902    {
1903        self.builder.config.cors.allowed_methods = methods.into_iter().map(Into::into).collect();
1904        self
1905    }
1906
1907    /// Allow specific headers.
1908    pub fn allow_headers<I, S>(mut self, headers: I) -> Self
1909    where
1910        I: IntoIterator<Item = S>,
1911        S: Into<String>,
1912    {
1913        self.builder.config.cors.allowed_headers = headers.into_iter().map(Into::into).collect();
1914        self
1915    }
1916
1917    /// Set max age for preflight requests.
1918    pub fn max_age(mut self, seconds: u32) -> Self {
1919        self.builder.config.cors.max_age_secs = seconds;
1920        self
1921    }
1922
1923    /// Finish CORS configuration and return to the main builder.
1924    pub fn done(self) -> AuthConfigBuilder {
1925        self.builder
1926    }
1927}
1928
1929/// Builder for audit configuration.
1930#[derive(Debug)]
1931pub struct AuditConfigBuilder {
1932    builder: AuthConfigBuilder,
1933}
1934
1935impl AuditConfigBuilder {
1936    /// Enable audit logging.
1937    pub fn enabled(mut self) -> Self {
1938        self.builder.config.audit.enabled = true;
1939        self
1940    }
1941
1942    /// Disable audit logging.
1943    pub fn disabled(mut self) -> Self {
1944        self.builder.config.audit.enabled = false;
1945        self
1946    }
1947
1948    /// Log successful authentications.
1949    pub fn log_success(mut self, enabled: bool) -> Self {
1950        self.builder.config.audit.log_success = enabled;
1951        self
1952    }
1953
1954    /// Log failed authentications.
1955    pub fn log_failures(mut self, enabled: bool) -> Self {
1956        self.builder.config.audit.log_failures = enabled;
1957        self
1958    }
1959
1960    /// Log permission checks.
1961    pub fn log_permissions(mut self, enabled: bool) -> Self {
1962        self.builder.config.audit.log_permissions = enabled;
1963        self
1964    }
1965
1966    /// Log token operations.
1967    pub fn log_tokens(mut self, enabled: bool) -> Self {
1968        self.builder.config.audit.log_tokens = enabled;
1969        self
1970    }
1971
1972    /// Finish audit configuration and return to the main builder.
1973    pub fn done(self) -> AuthConfigBuilder {
1974        self.builder
1975    }
1976}