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//! # #[tokio::main]
12//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! use auth_framework::prelude::*;
14//!
15//! // Simple JWT auth with environment variables
16//! let auth = AuthFramework::quick_start()
17//! .jwt_auth_from_env()
18//! .build().await?;
19//!
20//! // Web app with database
21//! let auth = AuthFramework::quick_start()
22//! .jwt_auth("your-secret-key")
23//! .with_postgres("postgresql://...")
24//! .with_axum()
25//! .build().await?;
26//! # Ok(())
27//! # }
28//! ```
29//!
30//! # Preset Configurations
31//!
32//! Use presets for common security and performance configurations:
33//!
34//! ```rust,no_run
35//! # #[tokio::main]
36//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! use auth_framework::prelude::*;
38//!
39//! let auth = AuthFramework::builder()
40//! .security_preset(SecurityPreset::HighSecurity)
41//! .performance_preset(PerformancePreset::LowLatency)
42//! .build().await?;
43//! # Ok(())
44//! # }
45//! ```
46//!
47//! # Use Case Templates
48//!
49//! Get started quickly with templates for common use cases:
50//!
51//! ```rust,no_run
52//! # #[tokio::main]
53//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
54//! use auth_framework::prelude::*;
55//!
56//! // Configure for web application
57//! let auth = AuthFramework::for_use_case(UseCasePreset::WebApp)
58//! .customize(|config| {
59//! config.token_lifetime = hours(24);
60//! config
61//! })
62//! .build().await?;
63//! # Ok(())
64//! # }
65//! ```
66
67use crate::{
68 AuthConfig, AuthError, AuthFramework,
69 config::{RateLimitConfig, SecurityConfig, StorageConfig},
70 prelude::{PerformancePreset, UseCasePreset, days, hours, minutes},
71 security::SecurityPreset,
72};
73use std::time::Duration;
74#[cfg(not(feature = "redis-storage"))]
75use tracing::warn;
76
77/// Main builder for constructing an [`AuthFramework`] instance.
78///
79/// Start with [`AuthFramework::builder()`] and chain sub-builders for
80/// JWT, storage, security, rate limiting, and audit configuration.
81///
82/// # Example
83///
84/// ```rust,no_run
85/// # #[tokio::main]
86/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
87/// use auth_framework::prelude::*;
88///
89/// let auth = AuthFramework::builder()
90/// .with_jwt().secret("my-secret-key-that-is-long-enough!!").issuer("myapp").done()
91/// .with_storage().memory().done()
92/// .security_preset(SecurityPreset::Balanced)
93/// .build().await?;
94/// # Ok(())
95/// # }
96/// ```
97pub struct AuthBuilder {
98 config: AuthConfig,
99 security_preset: Option<SecurityPreset>,
100 performance_preset: Option<PerformancePreset>,
101 use_case_preset: Option<UseCasePreset>,
102 storage_pool_size: Option<u32>,
103 /// Optional custom storage instance supplied by caller (`Arc<dyn AuthStorage>`)
104 custom_storage: Option<std::sync::Arc<dyn crate::storage::AuthStorage>>,
105}
106
107/// Quick start builder for common authentication setups.
108///
109/// Provides the fastest path to a working [`AuthFramework`] by combining
110/// authentication method, storage, and framework integrations in a single
111/// fluent chain. Start via [`AuthFramework::quick_start()`].
112///
113/// # Example
114///
115/// ```rust,no_run
116/// # #[tokio::main]
117/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
118/// use auth_framework::prelude::*;
119///
120/// let auth = AuthFramework::quick_start()
121/// .jwt_auth("my-secret-key-that-is-long-enough!!")
122/// .build().await?;
123/// # Ok(())
124/// # }
125/// ```
126#[derive(Debug)]
127pub struct QuickStartBuilder {
128 auth_method: Option<QuickStartAuth>,
129 storage: Option<QuickStartStorage>,
130 framework: Option<QuickStartFramework>,
131 security_level: SecurityPreset,
132}
133
134/// Authentication method selection for [`QuickStartBuilder`].
135#[derive(Debug)]
136pub enum QuickStartAuth {
137 Jwt {
138 secret: String,
139 },
140 JwtFromEnv,
141 OAuth2 {
142 client_id: String,
143 client_secret: String,
144 },
145 Combined {
146 jwt_secret: String,
147 oauth_client_id: String,
148 oauth_client_secret: String,
149 },
150}
151
152/// Storage backend selection for [`QuickStartBuilder`].
153#[derive(Debug)]
154pub enum QuickStartStorage {
155 Memory,
156 Postgres(String),
157 PostgresFromEnv,
158 Redis(String),
159 RedisFromEnv,
160}
161
162/// Web framework integration selection for [`QuickStartBuilder`].
163#[derive(Debug)]
164pub enum QuickStartFramework {
165 Axum,
166 ActixWeb,
167 Warp,
168}
169
170impl AuthFramework {
171 /// Create a new builder for the authentication framework
172 pub fn builder() -> AuthBuilder {
173 AuthBuilder::new()
174 }
175
176 /// Quick start builder for common setups
177 pub fn quick_start() -> QuickStartBuilder {
178 QuickStartBuilder::new()
179 }
180
181 /// Create a builder for a specific use case
182 pub fn for_use_case(use_case: UseCasePreset) -> AuthBuilder {
183 AuthBuilder::new().use_case_preset(use_case)
184 }
185
186 /// Create an authentication framework with preset configuration
187 pub fn preset(preset: SecurityPreset) -> AuthBuilder {
188 AuthBuilder::new().security_preset(preset)
189 }
190}
191
192impl AuthBuilder {
193 /// Create a new builder with default configuration.
194 ///
195 /// # Example
196 ///
197 /// ```rust
198 /// use auth_framework::builders::AuthBuilder;
199 ///
200 /// let builder = AuthBuilder::new();
201 /// ```
202 pub fn new() -> Self {
203 Self {
204 config: AuthConfig::default(),
205 security_preset: None,
206 performance_preset: None,
207 use_case_preset: None,
208 storage_pool_size: None,
209 custom_storage: None,
210 }
211 }
212
213 /// Apply a security preset.
214 ///
215 /// # Example
216 ///
217 /// ```rust,no_run
218 /// use auth_framework::prelude::*;
219 ///
220 /// let builder = AuthFramework::builder()
221 /// .security_preset(SecurityPreset::HighSecurity);
222 /// ```
223 pub fn security_preset(mut self, preset: SecurityPreset) -> Self {
224 self.security_preset = Some(preset);
225 self
226 }
227
228 /// Apply a performance preset.
229 ///
230 /// # Example
231 ///
232 /// ```rust,no_run
233 /// use auth_framework::prelude::*;
234 ///
235 /// let builder = AuthFramework::builder()
236 /// .performance_preset(PerformancePreset::LowLatency);
237 /// ```
238 pub fn performance_preset(mut self, preset: PerformancePreset) -> Self {
239 self.performance_preset = Some(preset);
240 self
241 }
242
243 /// Apply a use case preset.
244 ///
245 /// # Example
246 ///
247 /// ```rust,no_run
248 /// use auth_framework::prelude::*;
249 ///
250 /// let builder = AuthFramework::builder()
251 /// .use_case_preset(UseCasePreset::WebApp);
252 /// ```
253 pub fn use_case_preset(mut self, preset: UseCasePreset) -> Self {
254 self.use_case_preset = Some(preset);
255 self
256 }
257
258 /// Configure JWT authentication.
259 ///
260 /// Returns a [`JwtBuilder`] sub-builder. Call `.done()` to return.
261 ///
262 /// # Example
263 ///
264 /// ```rust,no_run
265 /// use auth_framework::prelude::*;
266 ///
267 /// let builder = AuthFramework::builder()
268 /// .with_jwt().secret("my-long-secret-key-32-chars-min!!").done();
269 /// ```
270 pub fn with_jwt(self) -> JwtBuilder {
271 JwtBuilder::new(self)
272 }
273
274 /// Configure OAuth2 authentication.
275 ///
276 /// Returns an [`OAuth2Builder`] sub-builder. Call `.done()` to return.
277 ///
278 /// # Example
279 ///
280 /// ```rust,no_run
281 /// use auth_framework::prelude::*;
282 ///
283 /// let builder = AuthFramework::builder()
284 /// .with_oauth2().client_id("id").client_secret("secret").done();
285 /// ```
286 pub fn with_oauth2(self) -> OAuth2Builder {
287 OAuth2Builder::new(self)
288 }
289
290 /// Configure storage backend.
291 ///
292 /// Returns a [`StorageBuilder`] sub-builder. Call `.done()` to return.
293 ///
294 /// # Example
295 ///
296 /// ```rust,no_run
297 /// use auth_framework::prelude::*;
298 ///
299 /// let builder = AuthFramework::builder()
300 /// .with_storage().memory().done();
301 /// ```
302 pub fn with_storage(self) -> StorageBuilder {
303 StorageBuilder::new(self)
304 }
305
306 /// Configure rate limiting.
307 ///
308 /// Returns a [`RateLimitBuilder`] sub-builder. Call `.done()` to return.
309 ///
310 /// # Example
311 ///
312 /// ```rust,no_run
313 /// use auth_framework::prelude::*;
314 /// use std::time::Duration;
315 ///
316 /// let builder = AuthFramework::builder()
317 /// .with_rate_limiting().per_ip((200, Duration::from_secs(60))).done();
318 /// ```
319 pub fn with_rate_limiting(self) -> RateLimitBuilder {
320 RateLimitBuilder::new(self)
321 }
322
323 /// Configure security settings.
324 ///
325 /// Returns a [`SecurityBuilder`] sub-builder. Call `.done()` to return.
326 ///
327 /// # Example
328 ///
329 /// ```rust,no_run
330 /// use auth_framework::prelude::*;
331 ///
332 /// let builder = AuthFramework::builder()
333 /// .with_security().min_password_length(12).secure_cookies(true).done();
334 /// ```
335 pub fn with_security(self) -> SecurityBuilder {
336 SecurityBuilder::new(self)
337 }
338
339 /// Configure audit logging.
340 ///
341 /// Returns an [`AuditBuilder`] sub-builder. Call `.done()` to return.
342 ///
343 /// # Example
344 ///
345 /// ```rust,no_run
346 /// use auth_framework::prelude::*;
347 ///
348 /// let builder = AuthFramework::builder()
349 /// .with_audit().enabled(true).log_success(true).done();
350 /// ```
351 pub fn with_audit(self) -> AuditBuilder {
352 AuditBuilder::new(self)
353 }
354
355 /// Customize configuration with a closure.
356 ///
357 /// # Example
358 ///
359 /// ```rust,no_run
360 /// use auth_framework::prelude::*;
361 /// use std::time::Duration;
362 ///
363 /// let builder = AuthFramework::builder()
364 /// .customize(|config| {
365 /// config.token_lifetime = Duration::from_secs(7200);
366 /// config
367 /// });
368 /// ```
369 pub fn customize<F>(mut self, f: F) -> Self
370 where
371 F: FnOnce(&mut AuthConfig) -> &mut AuthConfig,
372 {
373 f(&mut self.config);
374 self
375 }
376
377 /// Build the authentication framework.
378 ///
379 /// Applies presets, validates configuration, initializes storage,
380 /// and returns a ready-to-use [`AuthFramework`] instance.
381 ///
382 /// # Example
383 ///
384 /// ```rust,no_run
385 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
386 /// use auth_framework::prelude::*;
387 ///
388 /// let auth = AuthFramework::builder()
389 /// .with_jwt().secret("my-long-secret-key-32-chars-min!!").done()
390 /// .with_storage().memory().done()
391 /// .build().await?;
392 /// # Ok(()) }
393 /// ```
394 pub async fn build(mut self) -> Result<AuthFramework, AuthError> {
395 // Apply presets before building
396 if let Some(preset) = self.security_preset.take() {
397 self.config.security = self.apply_security_preset(preset);
398 }
399
400 if let Some(preset) = self.performance_preset.take() {
401 self.apply_performance_preset(preset);
402 }
403
404 if let Some(preset) = self.use_case_preset.take() {
405 self.apply_use_case_preset(preset);
406 }
407
408 // Validate configuration
409 self.config.validate()?;
410
411 // Create and initialize framework
412 // If a custom storage was provided via the builder, we'll construct a framework
413 // and replace its storage before initialization so managers use the custom storage.
414 let config = self.config.clone();
415 let mut framework = AuthFramework::new(self.config);
416 if let Some(storage) = self.custom_storage.take() {
417 framework.replace_storage(storage);
418 } else if let Some(pool_size) = self.storage_pool_size {
419 let storage =
420 crate::storage::factory::build_storage_backend(&config.storage, Some(pool_size))
421 .await?;
422 framework.replace_storage(storage);
423 }
424 framework.initialize().await?;
425
426 Ok(framework)
427 }
428
429 fn apply_security_preset(&self, preset: SecurityPreset) -> SecurityConfig {
430 match preset {
431 SecurityPreset::Development => SecurityConfig::development(),
432 SecurityPreset::Balanced => SecurityConfig::default(),
433 SecurityPreset::HighSecurity | SecurityPreset::Paranoid => SecurityConfig::secure(),
434 }
435 }
436
437 fn apply_performance_preset(&mut self, preset: PerformancePreset) {
438 match preset {
439 PerformancePreset::HighThroughput => {
440 // Optimize for throughput
441 self.config.rate_limiting.max_requests = 1000;
442 self.config.rate_limiting.window = Duration::from_secs(60);
443 }
444 PerformancePreset::LowLatency => {
445 // Optimize for latency
446 self.config.token_lifetime = hours(1);
447 self.config.rate_limiting.max_requests = 100;
448 self.config.rate_limiting.window = Duration::from_secs(60);
449 }
450 PerformancePreset::LowMemory => {
451 // Optimize for memory usage
452 self.config.token_lifetime = minutes(15);
453 self.config.refresh_token_lifetime = hours(2);
454 }
455 PerformancePreset::Balanced => {
456 // Keep defaults
457 }
458 }
459 }
460
461 fn apply_use_case_preset(&mut self, preset: UseCasePreset) {
462 match preset {
463 UseCasePreset::WebApp => {
464 self.config.token_lifetime = hours(24);
465 self.config.refresh_token_lifetime = days(7);
466 self.config.security.secure_cookies = true;
467 self.config.security.csrf_protection = true;
468 }
469 UseCasePreset::ApiService => {
470 self.config.token_lifetime = hours(1);
471 self.config.refresh_token_lifetime = hours(24);
472 self.config.rate_limiting.enabled = true;
473 self.config.rate_limiting.max_requests = 1000;
474 }
475 UseCasePreset::Microservices => {
476 self.config.token_lifetime = minutes(15);
477 self.config.refresh_token_lifetime = hours(1);
478 self.config.audit.enabled = true;
479 }
480 UseCasePreset::MobileBackend => {
481 self.config.token_lifetime = hours(1);
482 self.config.refresh_token_lifetime = days(30);
483 self.config.security.secure_cookies = false; // Mobile doesn't use cookies
484 }
485 UseCasePreset::Enterprise => {
486 self.config.enable_multi_factor = true;
487 self.config.security = SecurityConfig::secure();
488 self.config.audit.enabled = true;
489 self.config.audit.log_success = true;
490 self.config.audit.log_failures = true;
491 }
492 }
493 }
494}
495
496impl QuickStartBuilder {
497 fn new() -> Self {
498 Self {
499 auth_method: None,
500 storage: None,
501 framework: None,
502 security_level: SecurityPreset::Balanced,
503 }
504 }
505
506 /// Configure JWT authentication with a secret key.
507 ///
508 /// # Example
509 ///
510 /// ```rust,no_run
511 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
512 /// use auth_framework::prelude::*;
513 ///
514 /// let auth = AuthFramework::quick_start()
515 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
516 /// .build().await?;
517 /// # Ok(()) }
518 /// ```
519 pub fn jwt_auth(mut self, secret: impl Into<String>) -> Self {
520 self.auth_method = Some(QuickStartAuth::Jwt {
521 secret: secret.into(),
522 });
523 self
524 }
525
526 /// Configure JWT authentication from `JWT_SECRET` environment variable.
527 ///
528 /// # Example
529 ///
530 /// ```rust,no_run
531 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
532 /// use auth_framework::prelude::*;
533 ///
534 /// // Reads JWT_SECRET from the environment
535 /// let auth = AuthFramework::quick_start()
536 /// .jwt_auth_from_env()
537 /// .build().await?;
538 /// # Ok(()) }
539 /// ```
540 pub fn jwt_auth_from_env(mut self) -> Self {
541 self.auth_method = Some(QuickStartAuth::JwtFromEnv);
542 self
543 }
544
545 /// Configure OAuth2 authentication.
546 ///
547 /// # Example
548 ///
549 /// ```rust,no_run
550 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
551 /// use auth_framework::prelude::*;
552 ///
553 /// let auth = AuthFramework::quick_start()
554 /// .oauth2_auth("client-id", "client-secret")
555 /// .build().await?;
556 /// # Ok(()) }
557 /// ```
558 pub fn oauth2_auth(
559 mut self,
560 client_id: impl Into<String>,
561 client_secret: impl Into<String>,
562 ) -> Self {
563 self.auth_method = Some(QuickStartAuth::OAuth2 {
564 client_id: client_id.into(),
565 client_secret: client_secret.into(),
566 });
567 self
568 }
569
570 /// Configure both JWT and OAuth2 authentication.
571 ///
572 /// # Example
573 ///
574 /// ```rust,no_run
575 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
576 /// use auth_framework::prelude::*;
577 ///
578 /// let auth = AuthFramework::quick_start()
579 /// .combined_auth("jwt-secret-long-enough-32chars!!", "client-id", "secret")
580 /// .build().await?;
581 /// # Ok(()) }
582 /// ```
583 pub fn combined_auth(
584 mut self,
585 jwt_secret: impl Into<String>,
586 oauth_client_id: impl Into<String>,
587 oauth_client_secret: impl Into<String>,
588 ) -> Self {
589 self.auth_method = Some(QuickStartAuth::Combined {
590 jwt_secret: jwt_secret.into(),
591 oauth_client_id: oauth_client_id.into(),
592 oauth_client_secret: oauth_client_secret.into(),
593 });
594 self
595 }
596
597 /// Use PostgreSQL storage with connection string.
598 ///
599 /// # Example
600 ///
601 /// ```rust,no_run
602 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
603 /// use auth_framework::prelude::*;
604 ///
605 /// let auth = AuthFramework::quick_start()
606 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
607 /// .with_postgres("postgresql://user:pass@localhost/auth")
608 /// .build().await?;
609 /// # Ok(()) }
610 /// ```
611 pub fn with_postgres(mut self, connection_string: impl Into<String>) -> Self {
612 self.storage = Some(QuickStartStorage::Postgres(connection_string.into()));
613 self
614 }
615
616 /// Use PostgreSQL storage from `DATABASE_URL` environment variable.
617 ///
618 /// # Example
619 ///
620 /// ```rust,no_run
621 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
622 /// use auth_framework::prelude::*;
623 ///
624 /// let auth = AuthFramework::quick_start()
625 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
626 /// .with_postgres_from_env()
627 /// .build().await?;
628 /// # Ok(()) }
629 /// ```
630 pub fn with_postgres_from_env(mut self) -> Self {
631 self.storage = Some(QuickStartStorage::PostgresFromEnv);
632 self
633 }
634
635 /// Use Redis storage with connection string.
636 ///
637 /// # Example
638 ///
639 /// ```rust,no_run
640 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
641 /// use auth_framework::prelude::*;
642 ///
643 /// let auth = AuthFramework::quick_start()
644 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
645 /// .with_redis("redis://localhost:6379")
646 /// .build().await?;
647 /// # Ok(()) }
648 /// ```
649 pub fn with_redis(mut self, connection_string: impl Into<String>) -> Self {
650 self.storage = Some(QuickStartStorage::Redis(connection_string.into()));
651 self
652 }
653
654 /// Use Redis storage from `REDIS_URL` environment variable.
655 ///
656 /// # Example
657 ///
658 /// ```rust,no_run
659 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
660 /// use auth_framework::prelude::*;
661 ///
662 /// let auth = AuthFramework::quick_start()
663 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
664 /// .with_redis_from_env()
665 /// .build().await?;
666 /// # Ok(()) }
667 /// ```
668 pub fn with_redis_from_env(mut self) -> Self {
669 self.storage = Some(QuickStartStorage::RedisFromEnv);
670 self
671 }
672
673 /// Use in-memory storage (development only).
674 ///
675 /// # Example
676 ///
677 /// ```rust,no_run
678 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
679 /// use auth_framework::prelude::*;
680 ///
681 /// let auth = AuthFramework::quick_start()
682 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
683 /// .with_memory_storage()
684 /// .build().await?;
685 /// # Ok(()) }
686 /// ```
687 pub fn with_memory_storage(mut self) -> Self {
688 self.storage = Some(QuickStartStorage::Memory);
689 self
690 }
691
692 /// Configure for Axum web framework.
693 ///
694 /// # Example
695 ///
696 /// ```rust,no_run
697 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
698 /// use auth_framework::prelude::*;
699 ///
700 /// let auth = AuthFramework::quick_start()
701 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
702 /// .with_axum()
703 /// .build().await?;
704 /// # Ok(()) }
705 /// ```
706 pub fn with_axum(mut self) -> Self {
707 self.framework = Some(QuickStartFramework::Axum);
708 self
709 }
710
711 /// Configure for Actix Web framework.
712 ///
713 /// # Example
714 ///
715 /// ```rust,no_run
716 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
717 /// use auth_framework::prelude::*;
718 ///
719 /// let auth = AuthFramework::quick_start()
720 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
721 /// .with_actix()
722 /// .build().await?;
723 /// # Ok(()) }
724 /// ```
725 pub fn with_actix(mut self) -> Self {
726 self.framework = Some(QuickStartFramework::ActixWeb);
727 self
728 }
729
730 /// Configure for Warp web framework.
731 ///
732 /// # Example
733 ///
734 /// ```rust,no_run
735 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
736 /// use auth_framework::prelude::*;
737 ///
738 /// let auth = AuthFramework::quick_start()
739 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
740 /// .with_warp()
741 /// .build().await?;
742 /// # Ok(()) }
743 /// ```
744 pub fn with_warp(mut self) -> Self {
745 self.framework = Some(QuickStartFramework::Warp);
746 self
747 }
748
749 /// Set security level.
750 ///
751 /// # Example
752 ///
753 /// ```rust,no_run
754 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
755 /// use auth_framework::prelude::*;
756 ///
757 /// let auth = AuthFramework::quick_start()
758 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
759 /// .security_level(SecurityPreset::HighSecurity)
760 /// .build().await?;
761 /// # Ok(()) }
762 /// ```
763 pub fn security_level(mut self, level: SecurityPreset) -> Self {
764 self.security_level = level;
765 self
766 }
767
768 /// Build the authentication framework.
769 ///
770 /// Applies the configured auth method, storage, and security level,
771 /// then delegates to [`AuthBuilder::build`].
772 ///
773 /// # Example
774 ///
775 /// ```rust,no_run
776 /// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
777 /// use auth_framework::prelude::*;
778 ///
779 /// let auth = AuthFramework::quick_start()
780 /// .jwt_auth("my-long-secret-key-32-chars-min!!")
781 /// .build().await?;
782 /// # Ok(()) }
783 /// ```
784 pub async fn build(self) -> Result<AuthFramework, AuthError> {
785 let mut builder = AuthBuilder::new().security_preset(self.security_level);
786
787 // Configure authentication method
788 match self.auth_method {
789 Some(QuickStartAuth::Jwt { secret }) => {
790 builder = builder.with_jwt().secret(secret).done();
791 }
792 Some(QuickStartAuth::JwtFromEnv) => {
793 let secret = std::env::var("JWT_SECRET").map_err(|_| {
794 AuthError::config("JWT_SECRET environment variable is required")
795 })?;
796 builder = builder.with_jwt().secret(secret).done();
797 }
798 Some(QuickStartAuth::OAuth2 {
799 client_id,
800 client_secret,
801 }) => {
802 builder = builder
803 .with_oauth2()
804 .client_id(client_id)
805 .client_secret(client_secret)
806 .done();
807 }
808 Some(QuickStartAuth::Combined {
809 jwt_secret,
810 oauth_client_id,
811 oauth_client_secret,
812 }) => {
813 builder = builder
814 .with_jwt()
815 .secret(jwt_secret)
816 .done()
817 .with_oauth2()
818 .client_id(oauth_client_id)
819 .client_secret(oauth_client_secret)
820 .done();
821 }
822 None => {
823 return Err(AuthError::config("Authentication method is required"));
824 }
825 }
826
827 // Configure storage
828 match self.storage {
829 Some(QuickStartStorage::Memory) => {
830 builder = builder.with_storage().memory().done();
831 }
832 Some(QuickStartStorage::Postgres(_conn_str)) => {
833 #[cfg(feature = "postgres-storage")]
834 {
835 builder = builder.with_storage().postgres(_conn_str).done();
836 }
837 #[cfg(not(feature = "postgres-storage"))]
838 {
839 warn!(
840 "PostgreSQL storage requested but the `postgres-storage` feature is not enabled; \
841 falling back to in-memory storage"
842 );
843 builder = builder.with_storage().memory().done();
844 }
845 }
846 Some(QuickStartStorage::PostgresFromEnv) => {
847 #[cfg(feature = "postgres-storage")]
848 {
849 let conn_str = std::env::var("DATABASE_URL").map_err(|_| {
850 AuthError::config("DATABASE_URL environment variable is required")
851 })?;
852 builder = builder.with_storage().postgres(conn_str).done();
853 }
854 #[cfg(not(feature = "postgres-storage"))]
855 {
856 warn!(
857 "PostgreSQL storage requested but the `postgres-storage` feature is not enabled; \
858 falling back to in-memory storage"
859 );
860 builder = builder.with_storage().memory().done();
861 }
862 }
863 Some(QuickStartStorage::Redis(_conn_str)) => {
864 #[cfg(feature = "redis-storage")]
865 {
866 builder = builder.with_storage().redis(_conn_str).done();
867 }
868 #[cfg(not(feature = "redis-storage"))]
869 {
870 warn!(
871 "Redis storage requested but the `redis-storage` feature is not enabled; \
872 falling back to in-memory storage"
873 );
874 builder = builder.with_storage().memory().done();
875 }
876 }
877 Some(QuickStartStorage::RedisFromEnv) => {
878 #[cfg(feature = "redis-storage")]
879 {
880 builder = builder.with_storage().redis_from_env().done();
881 }
882 #[cfg(not(feature = "redis-storage"))]
883 {
884 warn!(
885 "Redis storage requested but the `redis-storage` feature is not enabled; \
886 falling back to in-memory storage"
887 );
888 builder = builder.with_storage().memory().done();
889 }
890 }
891 None => {
892 // Default to memory storage for quick start
893 builder = builder.with_storage().memory().done();
894 }
895 }
896
897 builder.build().await
898 }
899}
900
901/// Sub-builder for JWT settings.
902///
903/// Entered via [`AuthBuilder::with_jwt()`]; call [`done()`](JwtBuilder::done)
904/// to return to the parent builder.
905pub struct JwtBuilder {
906 parent: AuthBuilder,
907 secret: Option<String>,
908 issuer: Option<String>,
909 audience: Option<String>,
910 token_lifetime: Option<Duration>,
911 refresh_token_lifetime: Option<Duration>,
912 algorithm: Option<crate::config::JwtAlgorithm>,
913}
914
915impl JwtBuilder {
916 fn new(parent: AuthBuilder) -> Self {
917 Self {
918 parent,
919 secret: None,
920 issuer: None,
921 audience: None,
922 token_lifetime: None,
923 refresh_token_lifetime: None,
924 algorithm: None,
925 }
926 }
927
928 /// Set JWT secret key.
929 ///
930 /// Must be at least 32 characters for HMAC algorithms.
931 ///
932 /// # Example
933 ///
934 /// ```rust,no_run
935 /// use auth_framework::prelude::*;
936 ///
937 /// let builder = AuthFramework::builder()
938 /// .with_jwt().secret("my-long-secret-key-32-chars-min!!").done();
939 /// ```
940 pub fn secret(mut self, secret: impl Into<String>) -> Self {
941 self.secret = Some(secret.into());
942 self
943 }
944
945 /// Set JWT secret from environment variable.
946 ///
947 /// # Example
948 ///
949 /// ```rust,no_run
950 /// use auth_framework::prelude::*;
951 ///
952 /// let builder = AuthFramework::builder()
953 /// .with_jwt().secret_from_env("MY_JWT_SECRET").done();
954 /// ```
955 pub fn secret_from_env(mut self, env_var: &str) -> Self {
956 if let Ok(secret) = std::env::var(env_var) {
957 self.secret = Some(secret);
958 }
959 self
960 }
961
962 /// Set JWT issuer.
963 ///
964 /// # Example
965 ///
966 /// ```rust,no_run
967 /// use auth_framework::prelude::*;
968 ///
969 /// let builder = AuthFramework::builder()
970 /// .with_jwt().secret("secret-key-at-least-32-characters!!").issuer("my-service").done();
971 /// ```
972 pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
973 self.issuer = Some(issuer.into());
974 self
975 }
976
977 /// Set JWT audience.
978 ///
979 /// # Example
980 ///
981 /// ```rust,no_run
982 /// use auth_framework::prelude::*;
983 ///
984 /// let builder = AuthFramework::builder()
985 /// .with_jwt().secret("secret-key-at-least-32-characters!!").audience("my-api").done();
986 /// ```
987 pub fn audience(mut self, audience: impl Into<String>) -> Self {
988 self.audience = Some(audience.into());
989 self
990 }
991
992 /// Set token lifetime.
993 ///
994 /// # Example
995 ///
996 /// ```rust,no_run
997 /// use auth_framework::prelude::*;
998 /// use std::time::Duration;
999 ///
1000 /// let builder = AuthFramework::builder()
1001 /// .with_jwt()
1002 /// .secret("secret-key-at-least-32-characters!!")
1003 /// .token_lifetime(Duration::from_secs(1800))
1004 /// .done();
1005 /// ```
1006 pub fn token_lifetime(mut self, lifetime: Duration) -> Self {
1007 self.token_lifetime = Some(lifetime);
1008 self
1009 }
1010
1011 /// Set refresh token lifetime (defaults to 7 days).
1012 ///
1013 /// # Example
1014 ///
1015 /// ```rust,no_run
1016 /// use auth_framework::prelude::*;
1017 /// use std::time::Duration;
1018 ///
1019 /// let builder = AuthFramework::builder()
1020 /// .with_jwt()
1021 /// .secret("secret-key-at-least-32-characters!!")
1022 /// .refresh_token_lifetime(Duration::from_secs(86400))
1023 /// .done();
1024 /// ```
1025 pub fn refresh_token_lifetime(mut self, lifetime: Duration) -> Self {
1026 self.refresh_token_lifetime = Some(lifetime);
1027 self
1028 }
1029
1030 /// Set the JWT signing algorithm (defaults to HS256).
1031 ///
1032 /// # Example
1033 ///
1034 /// ```rust,no_run
1035 /// use auth_framework::prelude::*;
1036 /// use auth_framework::config::JwtAlgorithm;
1037 ///
1038 /// let builder = AuthFramework::builder()
1039 /// .with_jwt()
1040 /// .secret("secret-key-at-least-32-characters!!")
1041 /// .algorithm(JwtAlgorithm::HS512)
1042 /// .done();
1043 /// ```
1044 pub fn algorithm(mut self, algorithm: crate::config::JwtAlgorithm) -> Self {
1045 self.algorithm = Some(algorithm);
1046 self
1047 }
1048
1049 /// Complete JWT configuration and return to main builder.
1050 ///
1051 /// # Example
1052 ///
1053 /// ```rust,no_run
1054 /// use auth_framework::prelude::*;
1055 ///
1056 /// let builder = AuthFramework::builder()
1057 /// .with_jwt().secret("secret-key-at-least-32-characters!!").done()
1058 /// .with_storage().memory().done();
1059 /// ```
1060 pub fn done(mut self) -> AuthBuilder {
1061 if let Some(secret) = self.secret {
1062 self.parent.config.secret = Some(secret);
1063 }
1064 if let Some(issuer) = self.issuer {
1065 self.parent.config.issuer = issuer;
1066 }
1067 if let Some(audience) = self.audience {
1068 self.parent.config.audience = audience;
1069 }
1070 if let Some(lifetime) = self.token_lifetime {
1071 self.parent.config.token_lifetime = lifetime;
1072 }
1073 if let Some(lifetime) = self.refresh_token_lifetime {
1074 self.parent.config.refresh_token_lifetime = lifetime;
1075 }
1076 if let Some(algorithm) = self.algorithm {
1077 self.parent.config.security.jwt_algorithm = algorithm;
1078 }
1079 self.parent
1080 }
1081}
1082
1083/// Sub-builder for OAuth 2.0 client credentials.
1084///
1085/// Entered via [`AuthBuilder::with_oauth2()`]; call [`done()`](OAuth2Builder::done)
1086/// to return to the parent builder.
1087pub struct OAuth2Builder {
1088 parent: AuthBuilder,
1089 client_id: Option<String>,
1090 client_secret: Option<String>,
1091 redirect_uri: Option<String>,
1092}
1093
1094impl OAuth2Builder {
1095 fn new(parent: AuthBuilder) -> Self {
1096 Self {
1097 parent,
1098 client_id: None,
1099 client_secret: None,
1100 redirect_uri: None,
1101 }
1102 }
1103
1104 /// Set OAuth2 client ID.
1105 ///
1106 /// # Example
1107 ///
1108 /// ```rust,no_run
1109 /// use auth_framework::prelude::*;
1110 ///
1111 /// let builder = AuthFramework::builder()
1112 /// .with_oauth2().client_id("my-client-id").done();
1113 /// ```
1114 pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
1115 self.client_id = Some(client_id.into());
1116 self
1117 }
1118
1119 /// Set OAuth2 client secret.
1120 ///
1121 /// # Example
1122 ///
1123 /// ```rust,no_run
1124 /// use auth_framework::prelude::*;
1125 ///
1126 /// let builder = AuthFramework::builder()
1127 /// .with_oauth2().client_id("id").client_secret("secret").done();
1128 /// ```
1129 pub fn client_secret(mut self, client_secret: impl Into<String>) -> Self {
1130 self.client_secret = Some(client_secret.into());
1131 self
1132 }
1133
1134 /// Set redirect URI.
1135 ///
1136 /// # Example
1137 ///
1138 /// ```rust,no_run
1139 /// use auth_framework::prelude::*;
1140 ///
1141 /// let builder = AuthFramework::builder()
1142 /// .with_oauth2()
1143 /// .client_id("id")
1144 /// .redirect_uri("https://example.com/callback")
1145 /// .done();
1146 /// ```
1147 pub fn redirect_uri(mut self, redirect_uri: impl Into<String>) -> Self {
1148 self.redirect_uri = Some(redirect_uri.into());
1149 self
1150 }
1151
1152 /// Configure Google OAuth2.
1153 ///
1154 /// Alias for [`client_id`](Self::client_id).
1155 ///
1156 /// # Example
1157 ///
1158 /// ```rust,no_run
1159 /// use auth_framework::prelude::*;
1160 ///
1161 /// let builder = AuthFramework::builder()
1162 /// .with_oauth2().google_client_id("123.apps.googleusercontent.com").done();
1163 /// ```
1164 pub fn google_client_id(self, client_id: impl Into<String>) -> Self {
1165 self.client_id(client_id)
1166 }
1167
1168 /// Configure GitHub OAuth2.
1169 ///
1170 /// Alias for [`client_id`](Self::client_id).
1171 ///
1172 /// # Example
1173 ///
1174 /// ```rust,no_run
1175 /// use auth_framework::prelude::*;
1176 ///
1177 /// let builder = AuthFramework::builder()
1178 /// .with_oauth2().github_client_id("Iv1.abc123").done();
1179 /// ```
1180 pub fn github_client_id(self, client_id: impl Into<String>) -> Self {
1181 self.client_id(client_id)
1182 }
1183
1184 /// Complete OAuth2 configuration and return to main builder.
1185 ///
1186 /// # Example
1187 ///
1188 /// ```rust,no_run
1189 /// use auth_framework::prelude::*;
1190 ///
1191 /// let builder = AuthFramework::builder()
1192 /// .with_oauth2().client_id("id").client_secret("secret").done();
1193 /// ```
1194 pub fn done(mut self) -> AuthBuilder {
1195 let mut oauth2_config = serde_json::Map::new();
1196 if let Some(client_id) = self.client_id {
1197 oauth2_config.insert(
1198 "client_id".to_string(),
1199 serde_json::Value::String(client_id),
1200 );
1201 }
1202 if let Some(client_secret) = self.client_secret {
1203 oauth2_config.insert(
1204 "client_secret".to_string(),
1205 serde_json::Value::String(client_secret),
1206 );
1207 }
1208 if let Some(redirect_uri) = self.redirect_uri {
1209 oauth2_config.insert(
1210 "redirect_uri".to_string(),
1211 serde_json::Value::String(redirect_uri),
1212 );
1213 }
1214 self.parent.config.method_configs.insert(
1215 "oauth2".to_string(),
1216 serde_json::Value::Object(oauth2_config),
1217 );
1218 self.parent
1219 }
1220}
1221
1222/// Sub-builder for storage backend selection.
1223///
1224/// Entered via [`AuthBuilder::with_storage()`]; call [`done()`](StorageBuilder::done)
1225/// to return to the parent builder.
1226pub struct StorageBuilder {
1227 parent: AuthBuilder,
1228}
1229
1230impl StorageBuilder {
1231 fn new(parent: AuthBuilder) -> Self {
1232 Self { parent }
1233 }
1234
1235 /// Use a custom storage instance (already initialized) instead of
1236 /// the built-in storage backends.
1237 ///
1238 /// Pass any type that implements [`AuthStorage`](crate::storage::AuthStorage)
1239 /// wrapped in an `Arc`. This is the extension point for third-party or
1240 /// proprietary databases (SurrealDB, FoundationDB, DynamoDB, etc.).
1241 ///
1242 /// # Example
1243 ///
1244 /// ```rust,ignore
1245 /// use std::sync::Arc;
1246 /// use auth_framework::prelude::*;
1247 /// use auth_framework::storage::AuthStorage;
1248 ///
1249 /// // Implement `AuthStorage` for your backend, then:
1250 /// let storage: Arc<dyn AuthStorage> = Arc::new(
1251 /// MySurrealStorage::connect("ws://localhost:8000").await?,
1252 /// );
1253 ///
1254 /// let auth = AuthFramework::builder()
1255 /// .with_storage()
1256 /// .custom(storage)
1257 /// .done()
1258 /// .build()
1259 /// .await?;
1260 /// ```
1261 pub fn custom(mut self, storage: std::sync::Arc<dyn crate::storage::AuthStorage>) -> Self {
1262 self.parent.custom_storage = Some(storage);
1263 self
1264 }
1265
1266 /// Configure in-memory storage.
1267 ///
1268 /// # Example
1269 ///
1270 /// ```rust,no_run
1271 /// use auth_framework::prelude::*;
1272 ///
1273 /// let builder = AuthFramework::builder()
1274 /// .with_storage().memory().done();
1275 /// ```
1276 pub fn memory(mut self) -> Self {
1277 self.parent.config.storage = StorageConfig::Memory;
1278 self
1279 }
1280
1281 /// Configure PostgreSQL storage.
1282 ///
1283 /// # Example
1284 ///
1285 /// ```rust,no_run
1286 /// use auth_framework::prelude::*;
1287 ///
1288 /// let builder = AuthFramework::builder()
1289 /// .with_storage().postgres("postgresql://user:pass@localhost/db").done();
1290 /// ```
1291 #[cfg(feature = "postgres-storage")]
1292 pub fn postgres(mut self, connection_string: impl Into<String>) -> Self {
1293 self.parent.config.storage = StorageConfig::Postgres {
1294 connection_string: connection_string.into(),
1295 table_prefix: "auth_".to_string(),
1296 };
1297 self
1298 }
1299
1300 /// Configure PostgreSQL storage from environment.
1301 ///
1302 /// Reads `DATABASE_URL` from the environment.
1303 ///
1304 /// # Example
1305 ///
1306 /// ```rust,no_run
1307 /// use auth_framework::prelude::*;
1308 ///
1309 /// let builder = AuthFramework::builder()
1310 /// .with_storage().postgres_from_env().done();
1311 /// ```
1312 #[cfg(feature = "postgres-storage")]
1313 pub fn postgres_from_env(mut self) -> Self {
1314 if let Ok(conn_str) = std::env::var("DATABASE_URL") {
1315 self = self.postgres(conn_str);
1316 }
1317 self
1318 }
1319
1320 /// Configure Redis storage.
1321 ///
1322 /// # Example
1323 ///
1324 /// ```rust,no_run
1325 /// use auth_framework::prelude::*;
1326 ///
1327 /// let builder = AuthFramework::builder()
1328 /// .with_storage().redis("redis://localhost:6379").done();
1329 /// ```
1330 #[cfg(feature = "redis-storage")]
1331 pub fn redis(mut self, url: impl Into<String>) -> Self {
1332 self.parent.config.storage = StorageConfig::Redis {
1333 url: url.into(),
1334 key_prefix: "auth:".to_string(),
1335 };
1336 self
1337 }
1338
1339 /// Configure Redis storage from environment.
1340 ///
1341 /// Reads `REDIS_URL` from the environment.
1342 ///
1343 /// # Example
1344 ///
1345 /// ```rust,no_run
1346 /// use auth_framework::prelude::*;
1347 ///
1348 /// let builder = AuthFramework::builder()
1349 /// .with_storage().redis_from_env().done();
1350 /// ```
1351 #[cfg(feature = "redis-storage")]
1352 pub fn redis_from_env(mut self) -> Self {
1353 if let Ok(url) = std::env::var("REDIS_URL") {
1354 self = self.redis(url);
1355 }
1356 self
1357 }
1358
1359 /// Configure SQLite storage
1360 #[cfg(feature = "sqlite-storage")]
1361 pub fn sqlite(mut self, connection_string: impl Into<String>) -> Self {
1362 self.parent.config.storage = StorageConfig::Sqlite {
1363 connection_string: connection_string.into(),
1364 };
1365 self
1366 }
1367
1368 /// Set connection pool size.
1369 ///
1370 /// # Example
1371 ///
1372 /// ```rust,no_run
1373 /// use auth_framework::prelude::*;
1374 ///
1375 /// let builder = AuthFramework::builder()
1376 /// .with_storage().memory().connection_pool_size(20).done();
1377 /// ```
1378 pub fn connection_pool_size(mut self, size: u32) -> Self {
1379 self.parent.storage_pool_size = Some(size);
1380 self
1381 }
1382
1383 /// Complete storage configuration and return to main builder.
1384 pub fn done(self) -> AuthBuilder {
1385 self.parent
1386 }
1387}
1388
1389/// Sub-builder for rate limiting policy.
1390///
1391/// Entered via [`AuthBuilder::with_rate_limit()`]; call
1392/// [`done()`](RateLimitBuilder::done) to return to the parent builder.
1393pub struct RateLimitBuilder {
1394 parent: AuthBuilder,
1395}
1396
1397impl RateLimitBuilder {
1398 fn new(parent: AuthBuilder) -> Self {
1399 Self { parent }
1400 }
1401
1402 /// Configure rate limiting per IP.
1403 ///
1404 /// # Example
1405 ///
1406 /// ```rust,no_run
1407 /// use auth_framework::prelude::*;
1408 /// use std::time::Duration;
1409 ///
1410 /// let builder = AuthFramework::builder()
1411 /// .with_rate_limiting().per_ip((200, Duration::from_secs(60))).done();
1412 /// ```
1413 pub fn per_ip(mut self, (requests, window): (u32, Duration)) -> Self {
1414 self.parent.config.rate_limiting = RateLimitConfig {
1415 enabled: true,
1416 max_requests: requests,
1417 window,
1418 burst: requests / 10,
1419 };
1420 self
1421 }
1422
1423 /// Disable rate limiting.
1424 ///
1425 /// # Example
1426 ///
1427 /// ```rust,no_run
1428 /// use auth_framework::prelude::*;
1429 ///
1430 /// let builder = AuthFramework::builder()
1431 /// .with_rate_limiting().disabled().done();
1432 /// ```
1433 pub fn disabled(mut self) -> Self {
1434 self.parent.config.rate_limiting.enabled = false;
1435 self
1436 }
1437
1438 /// Complete rate limiting configuration and return to main builder.
1439 pub fn done(self) -> AuthBuilder {
1440 self.parent
1441 }
1442}
1443
1444/// Sub-builder for security settings (passwords, cookies, CSRF).
1445///
1446/// Entered via [`AuthBuilder::with_security()`]; call
1447/// [`done()`](SecurityBuilder::done) to return to the parent builder.
1448pub struct SecurityBuilder {
1449 parent: AuthBuilder,
1450}
1451
1452impl SecurityBuilder {
1453 fn new(parent: AuthBuilder) -> Self {
1454 Self { parent }
1455 }
1456
1457 /// Set minimum password length.
1458 ///
1459 /// # Example
1460 ///
1461 /// ```rust,no_run
1462 /// use auth_framework::prelude::*;
1463 ///
1464 /// let builder = AuthFramework::builder()
1465 /// .with_security().min_password_length(12).done();
1466 /// ```
1467 pub fn min_password_length(mut self, length: usize) -> Self {
1468 self.parent.config.security.min_password_length = length;
1469 self
1470 }
1471
1472 /// Enable/disable password complexity requirements.
1473 ///
1474 /// # Example
1475 ///
1476 /// ```rust,no_run
1477 /// use auth_framework::prelude::*;
1478 ///
1479 /// let builder = AuthFramework::builder()
1480 /// .with_security().require_password_complexity(false).done();
1481 /// ```
1482 pub fn require_password_complexity(mut self, required: bool) -> Self {
1483 self.parent.config.security.require_password_complexity = required;
1484 self
1485 }
1486
1487 /// Enable/disable secure cookies.
1488 ///
1489 /// # Example
1490 ///
1491 /// ```rust,no_run
1492 /// use auth_framework::prelude::*;
1493 ///
1494 /// let builder = AuthFramework::builder()
1495 /// .with_security().secure_cookies(true).done();
1496 /// ```
1497 pub fn secure_cookies(mut self, enabled: bool) -> Self {
1498 self.parent.config.security.secure_cookies = enabled;
1499 self
1500 }
1501
1502 /// Complete security configuration and return to main builder.
1503 pub fn done(self) -> AuthBuilder {
1504 self.parent
1505 }
1506}
1507
1508/// Sub-builder for audit logging settings.
1509///
1510/// Entered via [`AuthBuilder::with_audit()`]; call
1511/// [`done()`](AuditBuilder::done) to return to the parent builder.
1512pub struct AuditBuilder {
1513 parent: AuthBuilder,
1514}
1515
1516impl AuditBuilder {
1517 fn new(parent: AuthBuilder) -> Self {
1518 Self { parent }
1519 }
1520
1521 /// Enable audit logging.
1522 ///
1523 /// # Example
1524 ///
1525 /// ```rust,no_run
1526 /// use auth_framework::prelude::*;
1527 ///
1528 /// let builder = AuthFramework::builder()
1529 /// .with_audit().enabled(true).done();
1530 /// ```
1531 pub fn enabled(mut self, enabled: bool) -> Self {
1532 self.parent.config.audit.enabled = enabled;
1533 self
1534 }
1535
1536 /// Log successful authentications.
1537 ///
1538 /// # Example
1539 ///
1540 /// ```rust,no_run
1541 /// use auth_framework::prelude::*;
1542 ///
1543 /// let builder = AuthFramework::builder()
1544 /// .with_audit().log_success(true).done();
1545 /// ```
1546 pub fn log_success(mut self, enabled: bool) -> Self {
1547 self.parent.config.audit.log_success = enabled;
1548 self
1549 }
1550
1551 /// Log failed authentications.
1552 ///
1553 /// # Example
1554 ///
1555 /// ```rust,no_run
1556 /// use auth_framework::prelude::*;
1557 ///
1558 /// let builder = AuthFramework::builder()
1559 /// .with_audit().log_failures(true).done();
1560 /// ```
1561 pub fn log_failures(mut self, enabled: bool) -> Self {
1562 self.parent.config.audit.log_failures = enabled;
1563 self
1564 }
1565
1566 /// Complete audit configuration and return to main builder.
1567 pub fn done(self) -> AuthBuilder {
1568 self.parent
1569 }
1570}
1571
1572impl Default for AuthBuilder {
1573 fn default() -> Self {
1574 Self::new()
1575 }
1576}