better_auth_core/config/advanced.rs
1use super::*;
2#[derive(Debug, Clone, PartialEq, Eq)]
3pub enum SameSite {
4 Strict,
5 Lax,
6 None,
7}
8
9impl std::fmt::Display for SameSite {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 match self {
12 Self::Strict => f.write_str("Strict"),
13 Self::Lax => f.write_str("Lax"),
14 Self::None => f.write_str("None"),
15 }
16 }
17}
18
19// ── Advanced configuration ──────────────────────────────────────────────
20
21/// Advanced configuration options (mirrors TS `advanced` block).
22#[derive(Debug, Clone, Default)]
23pub struct AdvancedConfig {
24 /// Trust forwarded host/protocol for URL resolution. Enable only behind a
25 /// proxy that replaces client-supplied forwarding headers. Defaults to false.
26 pub trust_forwarded_host: bool,
27
28 /// IP address extraction configuration.
29 pub ip_address: IpAddressConfig,
30
31 /// Explicit CSRF override. `None` preserves the pinned compatibility behavior
32 /// where disabling all origin checks also disables first-login CSRF checks.
33 pub disable_csrf_check: Option<bool>,
34
35 /// If `true`, callback / redirect target origin validation is skipped.
36 ///
37 /// This mirrors Better Auth TS `advanced.disableOriginCheck`.
38 /// Request-origin validation is also skipped. First-login Fetch Metadata
39 /// checks remain enabled when `disable_csrf_check` is explicitly `Some(false)`.
40 pub disable_origin_check: bool,
41
42 /// Skip origin validation for a literal path and its descendants. This does
43 /// not disable first-login cross-site navigation protection.
44 pub disable_origin_check_paths: Vec<String>,
45
46 /// Admit trailing slashes while resolving the original registered endpoint.
47 pub skip_trailing_slashes: bool,
48
49 /// Select the secure cookie name prefix and initial Secure attribute.
50 /// None uses the configured base URL protocol. Attribute overrides do not
51 /// change this choice; the serializer enforces Secure for reserved prefixes.
52 pub use_secure_cookies: Option<bool>,
53
54 /// Cross-subdomain cookie sharing configuration.
55 pub cross_sub_domain_cookies: Option<CrossSubDomainConfig>,
56
57 /// Per-cookie-name overrides (name, attributes, prefix).
58 ///
59 /// Keys are the *logical* cookie names (e.g. `"session_token"`,
60 /// `"csrf_token"`). Values specify the attributes to override.
61 pub cookies: HashMap<String, CookieOverride>,
62
63 /// Default cookie attributes applied to *every* cookie the library sets
64 /// (individual overrides in `cookies` take precedence).
65 pub default_cookie_attributes: CookieAttributes,
66
67 /// Optional prefix prepended to every cookie name (e.g. `"myapp"` →
68 /// `"myapp.session_token"`).
69 pub cookie_prefix: Option<String>,
70
71 /// Database-related advanced options.
72 pub database: AdvancedDatabaseConfig,
73
74 /// List of header names the framework trusts for extracting the
75 /// client's real IP when behind a proxy (e.g. `X-Forwarded-For`).
76 pub trusted_proxy_headers: Vec<String>,
77}
78
79/// IP-address extraction configuration.
80#[derive(Debug, Clone)]
81pub struct IpAddressConfig {
82 /// Ordered list of headers to check for the client IP.
83 /// Defaults to `["x-forwarded-for"]`. Header names are case insensitive.
84 pub headers: Vec<String>,
85
86 /// IP addresses or CIDRs removed from the right of a forwarded chain.
87 /// Invalid entries are ignored. Without valid entries, only a single
88 /// address is admitted. Deployments must prevent clients bypassing the
89 /// proxy and supplying their own trusted forwarding headers.
90 pub trusted_proxies: Vec<String>,
91
92 /// IPv6 grouping prefix; defaults to 64. Fractional values are floored,
93 /// negative values become zero, and values at least 128 or NaN preserve
94 /// the full address. IPv4-mapped addresses use IPv4 grouping.
95 pub ipv6_subnet: f64,
96
97 /// Fall back to localhost when no configured header resolves an address.
98 /// Defaults to true for NODE_ENV dev/development/test or a truthy TEST
99 /// environment flag. Applications may configure this explicitly.
100 pub localhost_fallback: bool,
101
102 /// If `true`, IP tracking is entirely disabled (no IP stored in sessions).
103 pub disable_ip_tracking: bool,
104}
105
106/// Configuration for sharing cookies across sub-domains.
107#[derive(Debug, Clone, Default)]
108pub struct CrossSubDomainConfig {
109 /// Explicit cookie domain (e.g. `".example.com"`). An empty value infers
110 /// the hostname of the configured or request-resolved base URL, without a port.
111 pub domain: String,
112}
113
114/// Overridable cookie attributes.
115#[derive(Debug, Clone, Default)]
116pub struct CookieAttributes {
117 /// Override `Secure` flag.
118 pub secure: Option<bool>,
119 /// Override `HttpOnly` flag.
120 pub http_only: Option<bool>,
121 /// Override `SameSite` policy.
122 pub same_site: Option<SameSite>,
123 /// Override `Path`.
124 pub path: Option<String>,
125 /// Override `Max-Age` (seconds).
126 pub max_age: Option<f64>,
127 /// Explicit expiry, checked at emission against the published 400-day limit.
128 pub expires: Option<chrono::DateTime<chrono::Utc>>,
129 /// Emit the published `Partitioned` attribute after SameSite.
130 pub partitioned: Option<bool>,
131 /// Override cookie `Domain`.
132 pub domain: Option<String>,
133}
134
135/// Per-cookie override entry.
136#[derive(Debug, Clone, Default)]
137pub struct CookieOverride {
138 /// Custom name to use instead of the logical name.
139 pub name: Option<String>,
140 /// Attribute overrides for this cookie.
141 pub attributes: CookieAttributes,
142}
143
144/// Database-related advanced options.
145#[derive(Debug, Clone)]
146pub struct AdvancedDatabaseConfig {
147 /// Default `LIMIT` for "find many" queries.
148 pub default_find_many_limit: usize,
149
150 /// Declares that the database uses numeric IDs, as upstream's
151 /// `useNumberId`. IDs are always generated as strings; this only makes
152 /// invitation email verification required by default, because numeric
153 /// invitation IDs are guessable.
154 pub use_number_id: bool,
155}
156
157impl Default for IpAddressConfig {
158 fn default() -> Self {
159 Self {
160 headers: vec!["x-forwarded-for".to_owned()],
161 trusted_proxies: Vec::new(),
162 ipv6_subnet: 64.0,
163 localhost_fallback: matches!(
164 std::env::var("NODE_ENV").as_deref(),
165 Ok("dev" | "development" | "test")
166 ) || std::env::var("TEST")
167 .is_ok_and(|value| !matches!(value.as_str(), "" | "false")),
168 disable_ip_tracking: false,
169 }
170 }
171}
172
173impl Default for AdvancedDatabaseConfig {
174 fn default() -> Self {
175 Self {
176 default_find_many_limit: 100,
177 use_number_id: false,
178 }
179 }
180}