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