better_auth/core/
config.rs1use std::sync::Arc;
2use chrono::Duration;
3use crate::adapters::DatabaseAdapter;
4use crate::error::AuthError;
5
6#[derive(Clone)]
8pub struct AuthConfig {
9 pub secret: String,
11
12 pub base_url: String,
14
15 pub database: Option<Arc<dyn DatabaseAdapter>>,
17
18 pub session: SessionConfig,
20
21 pub jwt: JwtConfig,
23
24 pub password: PasswordConfig,
26}
27
28#[derive(Debug, Clone)]
30pub struct SessionConfig {
31 pub expires_in: Duration,
33
34 pub update_age: bool,
36
37 pub cookie_name: String,
39
40 pub cookie_secure: bool,
42 pub cookie_http_only: bool,
43 pub cookie_same_site: SameSite,
44}
45
46#[derive(Debug, Clone)]
48pub struct JwtConfig {
49 pub expires_in: Duration,
51
52 pub algorithm: String,
54
55 pub issuer: Option<String>,
57
58 pub audience: Option<String>,
60}
61
62#[derive(Debug, Clone)]
64pub struct PasswordConfig {
65 pub min_length: usize,
67
68 pub require_uppercase: bool,
70
71 pub require_lowercase: bool,
73
74 pub require_numbers: bool,
76
77 pub require_special: bool,
79
80 pub argon2_config: Argon2Config,
82}
83
84#[derive(Debug, Clone)]
86pub struct Argon2Config {
87 pub memory_cost: u32,
88 pub time_cost: u32,
89 pub parallelism: u32,
90}
91
92#[derive(Debug, Clone)]
93pub enum SameSite {
94 Strict,
95 Lax,
96 None,
97}
98
99impl Default for AuthConfig {
100 fn default() -> Self {
101 Self {
102 secret: String::new(),
103 base_url: "http://localhost:3000".to_string(),
104 database: None,
105 session: SessionConfig::default(),
106 jwt: JwtConfig::default(),
107 password: PasswordConfig::default(),
108 }
109 }
110}
111
112impl Default for SessionConfig {
113 fn default() -> Self {
114 Self {
115 expires_in: Duration::hours(24 * 7), update_age: true,
117 cookie_name: "better-auth.session-token".to_string(),
118 cookie_secure: true,
119 cookie_http_only: true,
120 cookie_same_site: SameSite::Lax,
121 }
122 }
123}
124
125impl Default for JwtConfig {
126 fn default() -> Self {
127 Self {
128 expires_in: Duration::hours(24), algorithm: "HS256".to_string(),
130 issuer: None,
131 audience: None,
132 }
133 }
134}
135
136impl Default for PasswordConfig {
137 fn default() -> Self {
138 Self {
139 min_length: 8,
140 require_uppercase: false,
141 require_lowercase: false,
142 require_numbers: false,
143 require_special: false,
144 argon2_config: Argon2Config::default(),
145 }
146 }
147}
148
149impl Default for Argon2Config {
150 fn default() -> Self {
151 Self {
152 memory_cost: 4096, time_cost: 3, parallelism: 1, }
156 }
157}
158
159impl AuthConfig {
160 pub fn new(secret: impl Into<String>) -> Self {
161 Self {
162 secret: secret.into(),
163 ..Default::default()
164 }
165 }
166
167 pub fn base_url(mut self, url: impl Into<String>) -> Self {
168 self.base_url = url.into();
169 self
170 }
171
172 pub fn session_expires_in(mut self, duration: Duration) -> Self {
173 self.session.expires_in = duration;
174 self
175 }
176
177 pub fn jwt_expires_in(mut self, duration: Duration) -> Self {
178 self.jwt.expires_in = duration;
179 self
180 }
181
182 pub fn password_min_length(mut self, length: usize) -> Self {
183 self.password.min_length = length;
184 self
185 }
186
187 pub fn validate(&self) -> Result<(), AuthError> {
188 if self.secret.is_empty() {
189 return Err(AuthError::config("Secret key cannot be empty"));
190 }
191
192 if self.secret.len() < 32 {
193 return Err(AuthError::config("Secret key must be at least 32 characters"));
194 }
195
196 if self.database.is_none() {
197 return Err(AuthError::config("Database adapter is required"));
198 }
199
200 Ok(())
201 }
202}