use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[cfg(feature = "oauth")]
use std::collections::HashMap;
use super::key_rotation::KeyRotationConfig;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AuthConfig {
#[serde(default)]
pub password: PasswordConfig,
#[serde(default)]
pub tokens: TokenGenerationConfig,
#[serde(default)]
pub paseto: Option<PasetoGenerationConfig>,
#[cfg(feature = "jwt")]
#[serde(default)]
pub jwt: Option<JwtGenerationConfig>,
#[serde(default)]
pub refresh_tokens: RefreshTokenConfig,
#[serde(default)]
pub api_keys: Option<ApiKeyConfig>,
#[cfg(feature = "oauth")]
#[serde(default)]
pub oauth: Option<OAuthConfig>,
#[serde(default)]
pub key_rotation: Option<KeyRotationConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasswordConfig {
#[serde(default = "default_memory_cost")]
pub memory_cost_kib: u32,
#[serde(default = "default_time_cost")]
pub time_cost: u32,
#[serde(default = "default_parallelism")]
pub parallelism: u32,
#[serde(default = "default_min_length")]
pub min_password_length: usize,
}
impl Default for PasswordConfig {
fn default() -> Self {
Self {
memory_cost_kib: default_memory_cost(),
time_cost: default_time_cost(),
parallelism: default_parallelism(),
min_password_length: default_min_length(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenGenerationConfig {
#[serde(default = "default_access_token_lifetime")]
pub access_token_lifetime_secs: i64,
#[serde(default)]
pub issuer: Option<String>,
#[serde(default)]
pub audience: Option<String>,
#[serde(default = "default_true")]
pub include_jti: bool,
}
impl Default for TokenGenerationConfig {
fn default() -> Self {
Self {
access_token_lifetime_secs: default_access_token_lifetime(),
issuer: None,
audience: None,
include_jti: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasetoGenerationConfig {
#[serde(default = "default_paseto_version")]
pub version: String,
#[serde(default = "default_paseto_purpose")]
pub purpose: String,
pub key_path: PathBuf,
#[serde(default)]
pub issuer: Option<String>,
#[serde(default)]
pub audience: Option<String>,
}
#[cfg(feature = "jwt")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtGenerationConfig {
pub private_key_path: PathBuf,
pub algorithm: String,
#[serde(default)]
pub issuer: Option<String>,
#[serde(default)]
pub audience: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefreshTokenConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_refresh_lifetime")]
pub lifetime_secs: i64,
#[serde(default = "default_true")]
pub rotate_on_refresh: bool,
#[serde(default = "default_true")]
pub detect_reuse: bool,
#[serde(default = "default_storage_backend")]
pub storage: String,
}
impl Default for RefreshTokenConfig {
fn default() -> Self {
Self {
enabled: true,
lifetime_secs: default_refresh_lifetime(),
rotate_on_refresh: true,
detect_reuse: true,
storage: default_storage_backend(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKeyConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_api_key_prefix")]
pub prefix: String,
#[serde(default = "default_api_key_header")]
pub header: String,
#[serde(default)]
pub default_rate_limit: Option<u32>,
#[serde(default = "default_storage_backend")]
pub storage: String,
}
impl Default for ApiKeyConfig {
fn default() -> Self {
Self {
enabled: true,
prefix: default_api_key_prefix(),
header: default_api_key_header(),
default_rate_limit: None,
storage: default_storage_backend(),
}
}
}
#[cfg(feature = "oauth")]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OAuthConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_oauth_state_ttl")]
pub state_ttl_secs: u64,
#[serde(default)]
pub providers: HashMap<String, OAuthProviderConfig>,
}
#[cfg(feature = "oauth")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuthProviderConfig {
pub client_id: String,
pub client_secret: String,
pub redirect_uri: String,
#[serde(default)]
pub scopes: Vec<String>,
#[serde(default)]
pub authorization_endpoint: Option<String>,
#[serde(default)]
pub token_endpoint: Option<String>,
#[serde(default)]
pub userinfo_endpoint: Option<String>,
}
fn default_memory_cost() -> u32 {
65536 }
fn default_time_cost() -> u32 {
3
}
fn default_parallelism() -> u32 {
4
}
fn default_min_length() -> usize {
8
}
fn default_access_token_lifetime() -> i64 {
900 }
fn default_refresh_lifetime() -> i64 {
604800 }
fn default_true() -> bool {
true
}
fn default_storage_backend() -> String {
"redis".to_string()
}
fn default_paseto_version() -> String {
"v4".to_string()
}
fn default_paseto_purpose() -> String {
"local".to_string()
}
fn default_api_key_prefix() -> String {
"sk_live".to_string()
}
fn default_api_key_header() -> String {
"X-API-Key".to_string()
}
#[cfg(feature = "oauth")]
fn default_oauth_state_ttl() -> u64 {
600 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_password_config_defaults() {
let config = PasswordConfig::default();
assert_eq!(config.memory_cost_kib, 65536);
assert_eq!(config.time_cost, 3);
assert_eq!(config.parallelism, 4);
assert_eq!(config.min_password_length, 8);
}
#[test]
fn test_token_config_defaults() {
let config = TokenGenerationConfig::default();
assert_eq!(config.access_token_lifetime_secs, 900);
assert!(config.include_jti);
}
#[test]
fn test_refresh_token_config_defaults() {
let config = RefreshTokenConfig::default();
assert!(config.enabled);
assert_eq!(config.lifetime_secs, 604800);
assert!(config.rotate_on_refresh);
assert!(config.detect_reuse);
assert_eq!(config.storage, "redis");
}
#[test]
fn test_api_key_config_defaults() {
let config = ApiKeyConfig::default();
assert!(config.enabled);
assert_eq!(config.prefix, "sk_live");
assert_eq!(config.header, "X-API-Key");
}
}