use crate::{
AuthConfig, AuthError, AuthFramework,
config::{RateLimitConfig, SecurityConfig, StorageConfig},
prelude::{PerformancePreset, UseCasePreset, days, hours, minutes},
security::SecurityPreset,
};
use std::time::Duration;
#[derive(Debug)]
pub struct AuthBuilder {
config: AuthConfig,
security_preset: Option<SecurityPreset>,
performance_preset: Option<PerformancePreset>,
use_case_preset: Option<UseCasePreset>,
}
#[derive(Debug)]
pub struct QuickStartBuilder {
auth_method: Option<QuickStartAuth>,
storage: Option<QuickStartStorage>,
framework: Option<QuickStartFramework>,
security_level: SecurityPreset,
}
#[derive(Debug)]
pub enum QuickStartAuth {
Jwt {
secret: String,
},
JwtFromEnv,
OAuth2 {
client_id: String,
client_secret: String,
},
Combined {
jwt_secret: String,
oauth_client_id: String,
oauth_client_secret: String,
},
}
#[derive(Debug)]
pub enum QuickStartStorage {
Memory,
Postgres(String),
PostgresFromEnv,
Redis(String),
RedisFromEnv,
}
#[derive(Debug)]
pub enum QuickStartFramework {
Axum,
ActixWeb,
Warp,
}
impl AuthFramework {
pub fn builder() -> AuthBuilder {
AuthBuilder::new()
}
pub fn quick_start() -> QuickStartBuilder {
QuickStartBuilder::new()
}
pub fn for_use_case(use_case: UseCasePreset) -> AuthBuilder {
AuthBuilder::new().use_case_preset(use_case)
}
pub fn preset(preset: SecurityPreset) -> AuthBuilder {
AuthBuilder::new().security_preset(preset)
}
}
impl AuthBuilder {
pub fn new() -> Self {
Self {
config: AuthConfig::default(),
security_preset: None,
performance_preset: None,
use_case_preset: None,
}
}
pub fn security_preset(mut self, preset: SecurityPreset) -> Self {
self.security_preset = Some(preset);
self
}
pub fn performance_preset(mut self, preset: PerformancePreset) -> Self {
self.performance_preset = Some(preset);
self
}
pub fn use_case_preset(mut self, preset: UseCasePreset) -> Self {
self.use_case_preset = Some(preset);
self
}
pub fn with_jwt(self) -> JwtBuilder {
JwtBuilder::new(self)
}
pub fn with_oauth2(self) -> OAuth2Builder {
OAuth2Builder::new(self)
}
pub fn with_storage(self) -> StorageBuilder {
StorageBuilder::new(self)
}
pub fn with_rate_limiting(self) -> RateLimitBuilder {
RateLimitBuilder::new(self)
}
pub fn with_security(self) -> SecurityBuilder {
SecurityBuilder::new(self)
}
pub fn with_audit(self) -> AuditBuilder {
AuditBuilder::new(self)
}
pub fn customize<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut AuthConfig) -> &mut AuthConfig,
{
f(&mut self.config);
self
}
pub async fn build(mut self) -> Result<AuthFramework, AuthError> {
if let Some(preset) = self.security_preset.take() {
self.config.security = self.apply_security_preset(preset);
}
if let Some(preset) = self.performance_preset.take() {
self.apply_performance_preset(preset);
}
if let Some(preset) = self.use_case_preset.take() {
self.apply_use_case_preset(preset);
}
self.config.validate()?;
let mut framework = AuthFramework::new(self.config);
framework.initialize().await?;
Ok(framework)
}
fn apply_security_preset(&self, preset: SecurityPreset) -> SecurityConfig {
match preset {
SecurityPreset::Development => SecurityConfig::development(),
SecurityPreset::Balanced => SecurityConfig::default(),
SecurityPreset::HighSecurity | SecurityPreset::Paranoid => SecurityConfig::secure(),
}
}
fn apply_performance_preset(&mut self, preset: PerformancePreset) {
match preset {
PerformancePreset::HighThroughput => {
self.config.rate_limiting.max_requests = 1000;
self.config.rate_limiting.window = Duration::from_secs(60);
}
PerformancePreset::LowLatency => {
self.config.token_lifetime = hours(1);
self.config.rate_limiting.max_requests = 100;
self.config.rate_limiting.window = Duration::from_secs(60);
}
PerformancePreset::LowMemory => {
self.config.token_lifetime = minutes(15);
self.config.refresh_token_lifetime = hours(2);
}
PerformancePreset::Balanced => {
}
}
}
fn apply_use_case_preset(&mut self, preset: UseCasePreset) {
match preset {
UseCasePreset::WebApp => {
self.config.token_lifetime = hours(24);
self.config.refresh_token_lifetime = days(7);
self.config.security.secure_cookies = true;
self.config.security.csrf_protection = true;
}
UseCasePreset::ApiService => {
self.config.token_lifetime = hours(1);
self.config.refresh_token_lifetime = hours(24);
self.config.rate_limiting.enabled = true;
self.config.rate_limiting.max_requests = 1000;
}
UseCasePreset::Microservices => {
self.config.token_lifetime = minutes(15);
self.config.refresh_token_lifetime = hours(1);
self.config.audit.enabled = true;
}
UseCasePreset::MobileBackend => {
self.config.token_lifetime = hours(1);
self.config.refresh_token_lifetime = days(30);
self.config.security.secure_cookies = false; }
UseCasePreset::Enterprise => {
self.config.enable_multi_factor = true;
self.config.security = SecurityConfig::secure();
self.config.audit.enabled = true;
self.config.audit.log_success = true;
self.config.audit.log_failures = true;
}
}
}
}
impl QuickStartBuilder {
fn new() -> Self {
Self {
auth_method: None,
storage: None,
framework: None,
security_level: SecurityPreset::Balanced,
}
}
pub fn jwt_auth(mut self, secret: impl Into<String>) -> Self {
self.auth_method = Some(QuickStartAuth::Jwt {
secret: secret.into(),
});
self
}
pub fn jwt_auth_from_env(mut self) -> Self {
self.auth_method = Some(QuickStartAuth::JwtFromEnv);
self
}
pub fn oauth2_auth(
mut self,
client_id: impl Into<String>,
client_secret: impl Into<String>,
) -> Self {
self.auth_method = Some(QuickStartAuth::OAuth2 {
client_id: client_id.into(),
client_secret: client_secret.into(),
});
self
}
pub fn combined_auth(
mut self,
jwt_secret: impl Into<String>,
oauth_client_id: impl Into<String>,
oauth_client_secret: impl Into<String>,
) -> Self {
self.auth_method = Some(QuickStartAuth::Combined {
jwt_secret: jwt_secret.into(),
oauth_client_id: oauth_client_id.into(),
oauth_client_secret: oauth_client_secret.into(),
});
self
}
pub fn with_postgres(mut self, connection_string: impl Into<String>) -> Self {
self.storage = Some(QuickStartStorage::Postgres(connection_string.into()));
self
}
pub fn with_postgres_from_env(mut self) -> Self {
self.storage = Some(QuickStartStorage::PostgresFromEnv);
self
}
pub fn with_redis(mut self, connection_string: impl Into<String>) -> Self {
self.storage = Some(QuickStartStorage::Redis(connection_string.into()));
self
}
pub fn with_redis_from_env(mut self) -> Self {
self.storage = Some(QuickStartStorage::RedisFromEnv);
self
}
pub fn with_memory_storage(mut self) -> Self {
self.storage = Some(QuickStartStorage::Memory);
self
}
pub fn with_axum(mut self) -> Self {
self.framework = Some(QuickStartFramework::Axum);
self
}
pub fn with_actix(mut self) -> Self {
self.framework = Some(QuickStartFramework::ActixWeb);
self
}
pub fn with_warp(mut self) -> Self {
self.framework = Some(QuickStartFramework::Warp);
self
}
pub fn security_level(mut self, level: SecurityPreset) -> Self {
self.security_level = level;
self
}
pub async fn build(self) -> Result<AuthFramework, AuthError> {
let mut builder = AuthBuilder::new().security_preset(self.security_level);
match self.auth_method {
Some(QuickStartAuth::Jwt { secret }) => {
builder = builder.with_jwt().secret(secret).done();
}
Some(QuickStartAuth::JwtFromEnv) => {
let secret = std::env::var("JWT_SECRET").map_err(|_| {
AuthError::config("JWT_SECRET environment variable is required")
})?;
builder = builder.with_jwt().secret(secret).done();
}
Some(QuickStartAuth::OAuth2 {
client_id,
client_secret,
}) => {
builder = builder
.with_oauth2()
.client_id(client_id)
.client_secret(client_secret)
.done();
}
Some(QuickStartAuth::Combined {
jwt_secret,
oauth_client_id,
oauth_client_secret,
}) => {
builder = builder
.with_jwt()
.secret(jwt_secret)
.done()
.with_oauth2()
.client_id(oauth_client_id)
.client_secret(oauth_client_secret)
.done();
}
None => {
return Err(AuthError::config("Authentication method is required"));
}
}
match self.storage {
Some(QuickStartStorage::Memory) => {
builder = builder.with_storage().memory().done();
}
Some(QuickStartStorage::Postgres(conn_str)) => {
builder = builder.with_storage().postgres(conn_str).done();
}
Some(QuickStartStorage::PostgresFromEnv) => {
let conn_str = std::env::var("DATABASE_URL").map_err(|_| {
AuthError::config("DATABASE_URL environment variable is required")
})?;
builder = builder.with_storage().postgres(conn_str).done();
}
Some(QuickStartStorage::Redis(_conn_str)) => {
builder = builder.with_storage().memory().done();
}
Some(QuickStartStorage::RedisFromEnv) => {
builder = builder.with_storage().memory().done();
}
None => {
builder = builder.with_storage().memory().done();
}
}
builder.build().await
}
}
pub struct JwtBuilder {
parent: AuthBuilder,
secret: Option<String>,
issuer: Option<String>,
audience: Option<String>,
token_lifetime: Option<Duration>,
}
impl JwtBuilder {
fn new(parent: AuthBuilder) -> Self {
Self {
parent,
secret: None,
issuer: None,
audience: None,
token_lifetime: None,
}
}
pub fn secret(mut self, secret: impl Into<String>) -> Self {
self.secret = Some(secret.into());
self
}
pub fn secret_from_env(mut self, env_var: &str) -> Self {
if let Ok(secret) = std::env::var(env_var) {
self.secret = Some(secret);
}
self
}
pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
self.issuer = Some(issuer.into());
self
}
pub fn audience(mut self, audience: impl Into<String>) -> Self {
self.audience = Some(audience.into());
self
}
pub fn token_lifetime(mut self, lifetime: Duration) -> Self {
self.token_lifetime = Some(lifetime);
self
}
pub fn done(mut self) -> AuthBuilder {
if let Some(secret) = self.secret {
self.parent.config.secret = Some(secret);
}
if let Some(issuer) = self.issuer {
self.parent.config.issuer = issuer;
}
if let Some(audience) = self.audience {
self.parent.config.audience = audience;
}
if let Some(lifetime) = self.token_lifetime {
self.parent.config.token_lifetime = lifetime;
}
self.parent
}
}
pub struct OAuth2Builder {
parent: AuthBuilder,
client_id: Option<String>,
client_secret: Option<String>,
redirect_uri: Option<String>,
}
impl OAuth2Builder {
fn new(parent: AuthBuilder) -> Self {
Self {
parent,
client_id: None,
client_secret: None,
redirect_uri: None,
}
}
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
pub fn client_secret(mut self, client_secret: impl Into<String>) -> Self {
self.client_secret = Some(client_secret.into());
self
}
pub fn redirect_uri(mut self, redirect_uri: impl Into<String>) -> Self {
self.redirect_uri = Some(redirect_uri.into());
self
}
pub fn google_client_id(self, client_id: impl Into<String>) -> Self {
self.client_id(client_id)
}
pub fn github_client_id(self, client_id: impl Into<String>) -> Self {
self.client_id(client_id)
}
pub fn done(self) -> AuthBuilder {
self.parent
}
}
pub struct StorageBuilder {
parent: AuthBuilder,
}
impl StorageBuilder {
fn new(parent: AuthBuilder) -> Self {
Self { parent }
}
pub fn memory(mut self) -> Self {
self.parent.config.storage = StorageConfig::Memory;
self
}
#[cfg(feature = "postgres-storage")]
pub fn postgres(mut self, connection_string: impl Into<String>) -> Self {
self.parent.config.storage = StorageConfig::Postgres {
connection_string: connection_string.into(),
table_prefix: "auth_".to_string(),
};
self
}
#[cfg(feature = "postgres-storage")]
pub fn postgres_from_env(mut self) -> Self {
if let Ok(conn_str) = std::env::var("DATABASE_URL") {
self = self.postgres(conn_str);
}
self
}
#[cfg(feature = "redis-storage")]
pub fn redis(mut self, url: impl Into<String>) -> Self {
self.parent.config.storage = StorageConfig::Redis {
url: url.into(),
key_prefix: "auth:".to_string(),
};
self
}
#[cfg(feature = "redis-storage")]
pub fn redis_from_env(mut self) -> Self {
if let Ok(url) = std::env::var("REDIS_URL") {
self = self.redis(url);
}
self
}
pub fn connection_pool_size(self, _size: u32) -> Self {
self
}
pub fn done(self) -> AuthBuilder {
self.parent
}
}
pub struct RateLimitBuilder {
parent: AuthBuilder,
}
impl RateLimitBuilder {
fn new(parent: AuthBuilder) -> Self {
Self { parent }
}
pub fn per_ip(mut self, (requests, window): (u32, Duration)) -> Self {
self.parent.config.rate_limiting = RateLimitConfig {
enabled: true,
max_requests: requests,
window,
burst: requests / 10,
};
self
}
pub fn disabled(mut self) -> Self {
self.parent.config.rate_limiting.enabled = false;
self
}
pub fn done(self) -> AuthBuilder {
self.parent
}
}
pub struct SecurityBuilder {
parent: AuthBuilder,
}
impl SecurityBuilder {
fn new(parent: AuthBuilder) -> Self {
Self { parent }
}
pub fn min_password_length(mut self, length: usize) -> Self {
self.parent.config.security.min_password_length = length;
self
}
pub fn require_password_complexity(mut self, required: bool) -> Self {
self.parent.config.security.require_password_complexity = required;
self
}
pub fn secure_cookies(mut self, enabled: bool) -> Self {
self.parent.config.security.secure_cookies = enabled;
self
}
pub fn done(self) -> AuthBuilder {
self.parent
}
}
pub struct AuditBuilder {
parent: AuthBuilder,
}
impl AuditBuilder {
fn new(parent: AuthBuilder) -> Self {
Self { parent }
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.parent.config.audit.enabled = enabled;
self
}
pub fn log_success(mut self, enabled: bool) -> Self {
self.parent.config.audit.log_success = enabled;
self
}
pub fn log_failures(mut self, enabled: bool) -> Self {
self.parent.config.audit.log_failures = enabled;
self
}
pub fn done(self) -> AuthBuilder {
self.parent
}
}
impl Default for AuthBuilder {
fn default() -> Self {
Self::new()
}
}