auth_framework/
builders.rs

1//! Builder patterns and ergonomic helpers for the Auth Framework
2//!
3//! This module provides fluent builder APIs and helper functions to make
4//! common authentication setup tasks easier and more discoverable.
5//!
6//! # Quick Start Builders
7//!
8//! For the most common setups, use the quick start builders:
9//!
10//! ```rust,no_run
11//! use auth_framework::prelude::*;
12//!
13//! // Simple JWT auth with environment variables
14//! let auth = AuthFramework::quick_start()
15//!     .jwt_auth_from_env()
16//!     .build().await?;
17//!
18//! // Web app with database
19//! let auth = AuthFramework::quick_start()
20//!     .jwt_auth("your-secret-key")
21//!     .with_postgres("postgresql://...")
22//!     .with_axum()
23//!     .build().await?;
24//! ```
25//!
26//! # Preset Configurations
27//!
28//! Use presets for common security and performance configurations:
29//!
30//! ```rust,no_run
31//! use auth_framework::prelude::*;
32//!
33//! let auth = AuthFramework::new()
34//!     .security_preset(SecurityPreset::HighSecurity)
35//!     .performance_preset(PerformancePreset::LowLatency)
36//!     .build().await?;
37//! ```
38//!
39//! # Use Case Templates
40//!
41//! Get started quickly with templates for common use cases:
42//!
43//! ```rust,no_run
44//! use auth_framework::prelude::*;
45//!
46//! // Configure for web application
47//! let auth = AuthFramework::for_use_case(UseCasePreset::WebApp)
48//!     .customize(|config| {
49//!         config.token_lifetime(hours(24))
50//!               .enable_sessions(true)
51//!     })
52//!     .build().await?;
53//! ```
54
55use crate::{
56    AuthConfig, AuthError, AuthFramework,
57    config::{RateLimitConfig, SecurityConfig, StorageConfig},
58    prelude::{PerformancePreset, UseCasePreset, days, hours, minutes},
59    security::SecurityPreset,
60};
61use std::time::Duration;
62
63/// Main builder for AuthFramework with fluent API
64#[derive(Debug)]
65pub struct AuthBuilder {
66    config: AuthConfig,
67    security_preset: Option<SecurityPreset>,
68    performance_preset: Option<PerformancePreset>,
69    use_case_preset: Option<UseCasePreset>,
70}
71
72/// Quick start builder for common authentication setups
73#[derive(Debug)]
74pub struct QuickStartBuilder {
75    auth_method: Option<QuickStartAuth>,
76    storage: Option<QuickStartStorage>,
77    framework: Option<QuickStartFramework>,
78    security_level: SecurityPreset,
79}
80
81/// Authentication method configuration for quick start
82#[derive(Debug)]
83pub enum QuickStartAuth {
84    Jwt {
85        secret: String,
86    },
87    JwtFromEnv,
88    OAuth2 {
89        client_id: String,
90        client_secret: String,
91    },
92    Combined {
93        jwt_secret: String,
94        oauth_client_id: String,
95        oauth_client_secret: String,
96    },
97}
98
99/// Storage configuration for quick start
100#[derive(Debug)]
101pub enum QuickStartStorage {
102    Memory,
103    Postgres(String),
104    PostgresFromEnv,
105    Redis(String),
106    RedisFromEnv,
107}
108
109/// Web framework integration for quick start
110#[derive(Debug)]
111pub enum QuickStartFramework {
112    Axum,
113    ActixWeb,
114    Warp,
115}
116
117impl AuthFramework {
118    /// Create a new builder for the authentication framework
119    pub fn builder() -> AuthBuilder {
120        AuthBuilder::new()
121    }
122
123    /// Quick start builder for common setups
124    pub fn quick_start() -> QuickStartBuilder {
125        QuickStartBuilder::new()
126    }
127
128    /// Create a builder for a specific use case
129    pub fn for_use_case(use_case: UseCasePreset) -> AuthBuilder {
130        AuthBuilder::new().use_case_preset(use_case)
131    }
132
133    /// Create an authentication framework with preset configuration
134    pub fn preset(preset: SecurityPreset) -> AuthBuilder {
135        AuthBuilder::new().security_preset(preset)
136    }
137}
138
139impl AuthBuilder {
140    /// Create a new builder with default configuration
141    pub fn new() -> Self {
142        Self {
143            config: AuthConfig::default(),
144            security_preset: None,
145            performance_preset: None,
146            use_case_preset: None,
147        }
148    }
149
150    /// Apply a security preset
151    pub fn security_preset(mut self, preset: SecurityPreset) -> Self {
152        self.security_preset = Some(preset);
153        self
154    }
155
156    /// Apply a performance preset
157    pub fn performance_preset(mut self, preset: PerformancePreset) -> Self {
158        self.performance_preset = Some(preset);
159        self
160    }
161
162    /// Apply a use case preset
163    pub fn use_case_preset(mut self, preset: UseCasePreset) -> Self {
164        self.use_case_preset = Some(preset);
165        self
166    }
167
168    /// Configure JWT authentication
169    pub fn with_jwt(self) -> JwtBuilder {
170        JwtBuilder::new(self)
171    }
172
173    /// Configure OAuth2 authentication
174    pub fn with_oauth2(self) -> OAuth2Builder {
175        OAuth2Builder::new(self)
176    }
177
178    /// Configure storage backend
179    pub fn with_storage(self) -> StorageBuilder {
180        StorageBuilder::new(self)
181    }
182
183    /// Configure rate limiting
184    pub fn with_rate_limiting(self) -> RateLimitBuilder {
185        RateLimitBuilder::new(self)
186    }
187
188    /// Configure security settings
189    pub fn with_security(self) -> SecurityBuilder {
190        SecurityBuilder::new(self)
191    }
192
193    /// Configure audit logging
194    pub fn with_audit(self) -> AuditBuilder {
195        AuditBuilder::new(self)
196    }
197
198    /// Customize configuration with a closure
199    pub fn customize<F>(mut self, f: F) -> Self
200    where
201        F: FnOnce(&mut AuthConfig) -> &mut AuthConfig,
202    {
203        f(&mut self.config);
204        self
205    }
206
207    /// Build the authentication framework
208    pub async fn build(mut self) -> Result<AuthFramework, AuthError> {
209        // Apply presets before building
210        if let Some(preset) = self.security_preset.take() {
211            self.config.security = self.apply_security_preset(preset);
212        }
213
214        if let Some(preset) = self.performance_preset.take() {
215            self.apply_performance_preset(preset);
216        }
217
218        if let Some(preset) = self.use_case_preset.take() {
219            self.apply_use_case_preset(preset);
220        }
221
222        // Validate configuration
223        self.config.validate()?;
224
225        // Create and initialize framework
226        let mut framework = AuthFramework::new(self.config);
227        framework.initialize().await?;
228
229        Ok(framework)
230    }
231
232    fn apply_security_preset(&self, preset: SecurityPreset) -> SecurityConfig {
233        match preset {
234            SecurityPreset::Development => SecurityConfig::development(),
235            SecurityPreset::Balanced => SecurityConfig::default(),
236            SecurityPreset::HighSecurity | SecurityPreset::Paranoid => SecurityConfig::secure(),
237        }
238    }
239
240    fn apply_performance_preset(&mut self, preset: PerformancePreset) {
241        match preset {
242            PerformancePreset::HighThroughput => {
243                // Optimize for throughput
244                self.config.rate_limiting.max_requests = 1000;
245                self.config.rate_limiting.window = Duration::from_secs(60);
246            }
247            PerformancePreset::LowLatency => {
248                // Optimize for latency
249                self.config.token_lifetime = hours(1);
250                self.config.rate_limiting.max_requests = 100;
251                self.config.rate_limiting.window = Duration::from_secs(60);
252            }
253            PerformancePreset::LowMemory => {
254                // Optimize for memory usage
255                self.config.token_lifetime = minutes(15);
256                self.config.refresh_token_lifetime = hours(2);
257            }
258            PerformancePreset::Balanced => {
259                // Keep defaults
260            }
261        }
262    }
263
264    fn apply_use_case_preset(&mut self, preset: UseCasePreset) {
265        match preset {
266            UseCasePreset::WebApp => {
267                self.config.token_lifetime = hours(24);
268                self.config.refresh_token_lifetime = days(7);
269                self.config.security.secure_cookies = true;
270                self.config.security.csrf_protection = true;
271            }
272            UseCasePreset::ApiService => {
273                self.config.token_lifetime = hours(1);
274                self.config.refresh_token_lifetime = hours(24);
275                self.config.rate_limiting.enabled = true;
276                self.config.rate_limiting.max_requests = 1000;
277            }
278            UseCasePreset::Microservices => {
279                self.config.token_lifetime = minutes(15);
280                self.config.refresh_token_lifetime = hours(1);
281                self.config.audit.enabled = true;
282            }
283            UseCasePreset::MobileBackend => {
284                self.config.token_lifetime = hours(1);
285                self.config.refresh_token_lifetime = days(30);
286                self.config.security.secure_cookies = false; // Mobile doesn't use cookies
287            }
288            UseCasePreset::Enterprise => {
289                self.config.enable_multi_factor = true;
290                self.config.security = SecurityConfig::secure();
291                self.config.audit.enabled = true;
292                self.config.audit.log_success = true;
293                self.config.audit.log_failures = true;
294            }
295        }
296    }
297}
298
299impl QuickStartBuilder {
300    fn new() -> Self {
301        Self {
302            auth_method: None,
303            storage: None,
304            framework: None,
305            security_level: SecurityPreset::Balanced,
306        }
307    }
308
309    /// Configure JWT authentication with a secret key
310    pub fn jwt_auth(mut self, secret: impl Into<String>) -> Self {
311        self.auth_method = Some(QuickStartAuth::Jwt {
312            secret: secret.into(),
313        });
314        self
315    }
316
317    /// Configure JWT authentication from JWT_SECRET environment variable
318    pub fn jwt_auth_from_env(mut self) -> Self {
319        self.auth_method = Some(QuickStartAuth::JwtFromEnv);
320        self
321    }
322
323    /// Configure OAuth2 authentication
324    pub fn oauth2_auth(
325        mut self,
326        client_id: impl Into<String>,
327        client_secret: impl Into<String>,
328    ) -> Self {
329        self.auth_method = Some(QuickStartAuth::OAuth2 {
330            client_id: client_id.into(),
331            client_secret: client_secret.into(),
332        });
333        self
334    }
335
336    /// Configure both JWT and OAuth2 authentication
337    pub fn combined_auth(
338        mut self,
339        jwt_secret: impl Into<String>,
340        oauth_client_id: impl Into<String>,
341        oauth_client_secret: impl Into<String>,
342    ) -> Self {
343        self.auth_method = Some(QuickStartAuth::Combined {
344            jwt_secret: jwt_secret.into(),
345            oauth_client_id: oauth_client_id.into(),
346            oauth_client_secret: oauth_client_secret.into(),
347        });
348        self
349    }
350
351    /// Use PostgreSQL storage with connection string
352    pub fn with_postgres(mut self, connection_string: impl Into<String>) -> Self {
353        self.storage = Some(QuickStartStorage::Postgres(connection_string.into()));
354        self
355    }
356
357    /// Use PostgreSQL storage from DATABASE_URL environment variable
358    pub fn with_postgres_from_env(mut self) -> Self {
359        self.storage = Some(QuickStartStorage::PostgresFromEnv);
360        self
361    }
362
363    /// Use Redis storage with connection string
364    pub fn with_redis(mut self, connection_string: impl Into<String>) -> Self {
365        self.storage = Some(QuickStartStorage::Redis(connection_string.into()));
366        self
367    }
368
369    /// Use Redis storage from REDIS_URL environment variable
370    pub fn with_redis_from_env(mut self) -> Self {
371        self.storage = Some(QuickStartStorage::RedisFromEnv);
372        self
373    }
374
375    /// Use in-memory storage (development only)
376    pub fn with_memory_storage(mut self) -> Self {
377        self.storage = Some(QuickStartStorage::Memory);
378        self
379    }
380
381    /// Configure for Axum web framework
382    pub fn with_axum(mut self) -> Self {
383        self.framework = Some(QuickStartFramework::Axum);
384        self
385    }
386
387    /// Configure for Actix Web framework
388    pub fn with_actix(mut self) -> Self {
389        self.framework = Some(QuickStartFramework::ActixWeb);
390        self
391    }
392
393    /// Configure for Warp web framework
394    pub fn with_warp(mut self) -> Self {
395        self.framework = Some(QuickStartFramework::Warp);
396        self
397    }
398
399    /// Set security level
400    pub fn security_level(mut self, level: SecurityPreset) -> Self {
401        self.security_level = level;
402        self
403    }
404
405    /// Build the authentication framework
406    pub async fn build(self) -> Result<AuthFramework, AuthError> {
407        let mut builder = AuthBuilder::new().security_preset(self.security_level);
408
409        // Configure authentication method
410        match self.auth_method {
411            Some(QuickStartAuth::Jwt { secret }) => {
412                builder = builder.with_jwt().secret(secret).done();
413            }
414            Some(QuickStartAuth::JwtFromEnv) => {
415                let secret = std::env::var("JWT_SECRET").map_err(|_| {
416                    AuthError::config("JWT_SECRET environment variable is required")
417                })?;
418                builder = builder.with_jwt().secret(secret).done();
419            }
420            Some(QuickStartAuth::OAuth2 {
421                client_id,
422                client_secret,
423            }) => {
424                builder = builder
425                    .with_oauth2()
426                    .client_id(client_id)
427                    .client_secret(client_secret)
428                    .done();
429            }
430            Some(QuickStartAuth::Combined {
431                jwt_secret,
432                oauth_client_id,
433                oauth_client_secret,
434            }) => {
435                builder = builder
436                    .with_jwt()
437                    .secret(jwt_secret)
438                    .done()
439                    .with_oauth2()
440                    .client_id(oauth_client_id)
441                    .client_secret(oauth_client_secret)
442                    .done();
443            }
444            None => {
445                return Err(AuthError::config("Authentication method is required"));
446            }
447        }
448
449        // Configure storage
450        match self.storage {
451            Some(QuickStartStorage::Memory) => {
452                builder = builder.with_storage().memory().done();
453            }
454            Some(QuickStartStorage::Postgres(conn_str)) => {
455                builder = builder.with_storage().postgres(conn_str).done();
456            }
457            Some(QuickStartStorage::PostgresFromEnv) => {
458                let conn_str = std::env::var("DATABASE_URL").map_err(|_| {
459                    AuthError::config("DATABASE_URL environment variable is required")
460                })?;
461                builder = builder.with_storage().postgres(conn_str).done();
462            }
463            Some(QuickStartStorage::Redis(_conn_str)) => {
464                // Redis storage not yet implemented, fallback to memory
465                builder = builder.with_storage().memory().done();
466            }
467            Some(QuickStartStorage::RedisFromEnv) => {
468                // Redis storage not yet implemented, fallback to memory
469                builder = builder.with_storage().memory().done();
470            }
471            None => {
472                // Default to memory storage for quick start
473                builder = builder.with_storage().memory().done();
474            }
475        }
476
477        builder.build().await
478    }
479}
480
481/// JWT configuration builder
482pub struct JwtBuilder {
483    parent: AuthBuilder,
484    secret: Option<String>,
485    issuer: Option<String>,
486    audience: Option<String>,
487    token_lifetime: Option<Duration>,
488}
489
490impl JwtBuilder {
491    fn new(parent: AuthBuilder) -> Self {
492        Self {
493            parent,
494            secret: None,
495            issuer: None,
496            audience: None,
497            token_lifetime: None,
498        }
499    }
500
501    /// Set JWT secret key
502    pub fn secret(mut self, secret: impl Into<String>) -> Self {
503        self.secret = Some(secret.into());
504        self
505    }
506
507    /// Load JWT secret from environment variable
508    pub fn secret_from_env(mut self, env_var: &str) -> Self {
509        if let Ok(secret) = std::env::var(env_var) {
510            self.secret = Some(secret);
511        }
512        self
513    }
514
515    /// Set JWT issuer
516    pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
517        self.issuer = Some(issuer.into());
518        self
519    }
520
521    /// Set JWT audience
522    pub fn audience(mut self, audience: impl Into<String>) -> Self {
523        self.audience = Some(audience.into());
524        self
525    }
526
527    /// Set token lifetime
528    pub fn token_lifetime(mut self, lifetime: Duration) -> Self {
529        self.token_lifetime = Some(lifetime);
530        self
531    }
532
533    /// Complete JWT configuration and return to main builder
534    pub fn done(mut self) -> AuthBuilder {
535        if let Some(secret) = self.secret {
536            self.parent.config.secret = Some(secret);
537        }
538        if let Some(issuer) = self.issuer {
539            self.parent.config.issuer = issuer;
540        }
541        if let Some(audience) = self.audience {
542            self.parent.config.audience = audience;
543        }
544        if let Some(lifetime) = self.token_lifetime {
545            self.parent.config.token_lifetime = lifetime;
546        }
547        self.parent
548    }
549}
550
551/// OAuth2 configuration builder
552pub struct OAuth2Builder {
553    parent: AuthBuilder,
554    client_id: Option<String>,
555    client_secret: Option<String>,
556    redirect_uri: Option<String>,
557}
558
559impl OAuth2Builder {
560    fn new(parent: AuthBuilder) -> Self {
561        Self {
562            parent,
563            client_id: None,
564            client_secret: None,
565            redirect_uri: None,
566        }
567    }
568
569    /// Set OAuth2 client ID
570    pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
571        self.client_id = Some(client_id.into());
572        self
573    }
574
575    /// Set OAuth2 client secret
576    pub fn client_secret(mut self, client_secret: impl Into<String>) -> Self {
577        self.client_secret = Some(client_secret.into());
578        self
579    }
580
581    /// Set redirect URI
582    pub fn redirect_uri(mut self, redirect_uri: impl Into<String>) -> Self {
583        self.redirect_uri = Some(redirect_uri.into());
584        self
585    }
586
587    /// Configure Google OAuth2
588    pub fn google_client_id(self, client_id: impl Into<String>) -> Self {
589        self.client_id(client_id)
590    }
591
592    /// Configure GitHub OAuth2
593    pub fn github_client_id(self, client_id: impl Into<String>) -> Self {
594        self.client_id(client_id)
595    }
596
597    /// Complete OAuth2 configuration and return to main builder
598    pub fn done(self) -> AuthBuilder {
599        // OAuth2 configuration would be stored in method_configs
600        // This is a simplified version
601        self.parent
602    }
603}
604
605/// Storage configuration builder
606pub struct StorageBuilder {
607    parent: AuthBuilder,
608}
609
610impl StorageBuilder {
611    fn new(parent: AuthBuilder) -> Self {
612        Self { parent }
613    }
614
615    /// Configure in-memory storage
616    pub fn memory(mut self) -> Self {
617        self.parent.config.storage = StorageConfig::Memory;
618        self
619    }
620
621    /// Configure PostgreSQL storage
622    #[cfg(feature = "postgres-storage")]
623    pub fn postgres(mut self, connection_string: impl Into<String>) -> Self {
624        self.parent.config.storage = StorageConfig::Postgres {
625            connection_string: connection_string.into(),
626            table_prefix: "auth_".to_string(),
627        };
628        self
629    }
630
631    /// Configure PostgreSQL storage from environment
632    #[cfg(feature = "postgres-storage")]
633    pub fn postgres_from_env(mut self) -> Self {
634        if let Ok(conn_str) = std::env::var("DATABASE_URL") {
635            self = self.postgres(conn_str);
636        }
637        self
638    }
639
640    /// Configure Redis storage
641    #[cfg(feature = "redis-storage")]
642    pub fn redis(mut self, url: impl Into<String>) -> Self {
643        self.parent.config.storage = StorageConfig::Redis {
644            url: url.into(),
645            key_prefix: "auth:".to_string(),
646        };
647        self
648    }
649
650    /// Configure Redis storage from environment
651    #[cfg(feature = "redis-storage")]
652    pub fn redis_from_env(mut self) -> Self {
653        if let Ok(url) = std::env::var("REDIS_URL") {
654            self = self.redis(url);
655        }
656        self
657    }
658
659    /// Set connection pool size
660    pub fn connection_pool_size(self, _size: u32) -> Self {
661        // This would be implemented when storage supports connection pooling
662        self
663    }
664
665    /// Complete storage configuration and return to main builder
666    pub fn done(self) -> AuthBuilder {
667        self.parent
668    }
669}
670
671/// Rate limiting configuration builder
672pub struct RateLimitBuilder {
673    parent: AuthBuilder,
674}
675
676impl RateLimitBuilder {
677    fn new(parent: AuthBuilder) -> Self {
678        Self { parent }
679    }
680
681    /// Configure rate limiting per IP
682    pub fn per_ip(mut self, (requests, window): (u32, Duration)) -> Self {
683        self.parent.config.rate_limiting = RateLimitConfig {
684            enabled: true,
685            max_requests: requests,
686            window,
687            burst: requests / 10,
688        };
689        self
690    }
691
692    /// Disable rate limiting
693    pub fn disabled(mut self) -> Self {
694        self.parent.config.rate_limiting.enabled = false;
695        self
696    }
697
698    /// Complete rate limiting configuration and return to main builder
699    pub fn done(self) -> AuthBuilder {
700        self.parent
701    }
702}
703
704/// Security configuration builder
705pub struct SecurityBuilder {
706    parent: AuthBuilder,
707}
708
709impl SecurityBuilder {
710    fn new(parent: AuthBuilder) -> Self {
711        Self { parent }
712    }
713
714    /// Set minimum password length
715    pub fn min_password_length(mut self, length: usize) -> Self {
716        self.parent.config.security.min_password_length = length;
717        self
718    }
719
720    /// Enable/disable password complexity requirements
721    pub fn require_password_complexity(mut self, required: bool) -> Self {
722        self.parent.config.security.require_password_complexity = required;
723        self
724    }
725
726    /// Enable/disable secure cookies
727    pub fn secure_cookies(mut self, enabled: bool) -> Self {
728        self.parent.config.security.secure_cookies = enabled;
729        self
730    }
731
732    /// Complete security configuration and return to main builder
733    pub fn done(self) -> AuthBuilder {
734        self.parent
735    }
736}
737
738/// Audit configuration builder
739pub struct AuditBuilder {
740    parent: AuthBuilder,
741}
742
743impl AuditBuilder {
744    fn new(parent: AuthBuilder) -> Self {
745        Self { parent }
746    }
747
748    /// Enable audit logging
749    pub fn enabled(mut self, enabled: bool) -> Self {
750        self.parent.config.audit.enabled = enabled;
751        self
752    }
753
754    /// Log successful authentications
755    pub fn log_success(mut self, enabled: bool) -> Self {
756        self.parent.config.audit.log_success = enabled;
757        self
758    }
759
760    /// Log failed authentications
761    pub fn log_failures(mut self, enabled: bool) -> Self {
762        self.parent.config.audit.log_failures = enabled;
763        self
764    }
765
766    /// Complete audit configuration and return to main builder
767    pub fn done(self) -> AuthBuilder {
768        self.parent
769    }
770}
771
772impl Default for AuthBuilder {
773    fn default() -> Self {
774        Self::new()
775    }
776}