@better-auth/core 1.5.0-beta.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/README.md +17 -0
  2. package/dist/api/index.d.mts +144 -41
  3. package/dist/api/index.mjs +2 -1
  4. package/dist/api/index.mjs.map +1 -0
  5. package/dist/async_hooks/index.d.mts +2 -1
  6. package/dist/async_hooks/index.mjs +2 -1
  7. package/dist/async_hooks/index.mjs.map +1 -0
  8. package/dist/async_hooks/pure.index.d.mts +2 -1
  9. package/dist/async_hooks/pure.index.mjs +2 -1
  10. package/dist/async_hooks/pure.index.mjs.map +1 -0
  11. package/dist/context/endpoint-context.d.mts +2 -1
  12. package/dist/context/endpoint-context.mjs +4 -3
  13. package/dist/context/endpoint-context.mjs.map +1 -0
  14. package/dist/context/global.d.mts +2 -2
  15. package/dist/context/global.mjs +3 -2
  16. package/dist/context/global.mjs.map +1 -0
  17. package/dist/context/request-state.d.mts +2 -1
  18. package/dist/context/request-state.mjs +4 -3
  19. package/dist/context/request-state.mjs.map +1 -0
  20. package/dist/context/transaction.d.mts +2 -1
  21. package/dist/context/transaction.mjs +4 -3
  22. package/dist/context/transaction.mjs.map +1 -0
  23. package/dist/db/adapter/factory.d.mts +6 -13
  24. package/dist/db/adapter/factory.mjs +44 -57
  25. package/dist/db/adapter/factory.mjs.map +1 -0
  26. package/dist/db/adapter/get-default-field-name.d.mts +2 -1
  27. package/dist/db/adapter/get-default-field-name.mjs +3 -2
  28. package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
  29. package/dist/db/adapter/get-default-model-name.d.mts +2 -1
  30. package/dist/db/adapter/get-default-model-name.mjs +5 -4
  31. package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
  32. package/dist/db/adapter/get-field-attributes.d.mts +3 -2
  33. package/dist/db/adapter/get-field-attributes.mjs +2 -1
  34. package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
  35. package/dist/db/adapter/get-field-name.d.mts +2 -1
  36. package/dist/db/adapter/get-field-name.mjs +2 -1
  37. package/dist/db/adapter/get-field-name.mjs.map +1 -0
  38. package/dist/db/adapter/get-id-field.d.mts +3 -2
  39. package/dist/db/adapter/get-id-field.mjs +3 -2
  40. package/dist/db/adapter/get-id-field.mjs.map +1 -0
  41. package/dist/db/adapter/get-model-name.d.mts +2 -1
  42. package/dist/db/adapter/get-model-name.mjs +2 -1
  43. package/dist/db/adapter/get-model-name.mjs.map +1 -0
  44. package/dist/db/adapter/index.d.mts +10 -4
  45. package/dist/db/adapter/index.mjs +19 -2
  46. package/dist/db/adapter/index.mjs.map +1 -0
  47. package/dist/db/adapter/types.d.mts +3 -34
  48. package/dist/db/adapter/utils.d.mts +2 -1
  49. package/dist/db/adapter/utils.mjs +2 -1
  50. package/dist/db/adapter/utils.mjs.map +1 -0
  51. package/dist/db/get-tables.d.mts +2 -1
  52. package/dist/db/get-tables.mjs +46 -39
  53. package/dist/db/get-tables.mjs.map +1 -0
  54. package/dist/db/index.d.mts +7 -7
  55. package/dist/db/plugin.d.mts +2 -1
  56. package/dist/db/schema/account.d.mts +8 -4
  57. package/dist/db/schema/account.mjs +2 -1
  58. package/dist/db/schema/account.mjs.map +1 -0
  59. package/dist/db/schema/rate-limit.d.mts +8 -2
  60. package/dist/db/schema/rate-limit.mjs +2 -1
  61. package/dist/db/schema/rate-limit.mjs.map +1 -0
  62. package/dist/db/schema/session.d.mts +8 -4
  63. package/dist/db/schema/session.mjs +2 -1
  64. package/dist/db/schema/session.mjs.map +1 -0
  65. package/dist/db/schema/shared.d.mts +2 -1
  66. package/dist/db/schema/shared.mjs +2 -1
  67. package/dist/db/schema/shared.mjs.map +1 -0
  68. package/dist/db/schema/user.d.mts +8 -4
  69. package/dist/db/schema/user.mjs +2 -1
  70. package/dist/db/schema/user.mjs.map +1 -0
  71. package/dist/db/schema/verification.d.mts +8 -4
  72. package/dist/db/schema/verification.mjs +2 -1
  73. package/dist/db/schema/verification.mjs.map +1 -0
  74. package/dist/db/type.d.mts +28 -2
  75. package/dist/env/color-depth.d.mts +2 -1
  76. package/dist/env/color-depth.mjs +2 -1
  77. package/dist/env/color-depth.mjs.map +1 -0
  78. package/dist/env/env-impl.d.mts +3 -2
  79. package/dist/env/env-impl.mjs +9 -8
  80. package/dist/env/env-impl.mjs.map +1 -0
  81. package/dist/env/logger.d.mts +2 -1
  82. package/dist/env/logger.mjs +3 -2
  83. package/dist/env/logger.mjs.map +1 -0
  84. package/dist/error/codes.d.mts +64 -181
  85. package/dist/error/codes.mjs +6 -2
  86. package/dist/error/codes.mjs.map +1 -0
  87. package/dist/error/index.d.mts +2 -1
  88. package/dist/error/index.mjs +2 -1
  89. package/dist/error/index.mjs.map +1 -0
  90. package/dist/index.d.mts +5 -4
  91. package/dist/oauth2/client-credentials-token.d.mts +25 -3
  92. package/dist/oauth2/client-credentials-token.mjs +15 -2
  93. package/dist/oauth2/client-credentials-token.mjs.map +1 -0
  94. package/dist/oauth2/create-authorization-url.d.mts +5 -2
  95. package/dist/oauth2/create-authorization-url.mjs +3 -1
  96. package/dist/oauth2/create-authorization-url.mjs.map +1 -0
  97. package/dist/oauth2/index.d.mts +4 -4
  98. package/dist/oauth2/index.mjs +4 -4
  99. package/dist/oauth2/oauth-provider.d.mts +3 -2
  100. package/dist/oauth2/refresh-access-token.d.mts +24 -4
  101. package/dist/oauth2/refresh-access-token.mjs +20 -2
  102. package/dist/oauth2/refresh-access-token.mjs.map +1 -0
  103. package/dist/oauth2/utils.d.mts +2 -1
  104. package/dist/oauth2/utils.mjs +2 -1
  105. package/dist/oauth2/utils.mjs.map +1 -0
  106. package/dist/oauth2/validate-authorization-code.d.mts +37 -4
  107. package/dist/oauth2/validate-authorization-code.mjs +25 -13
  108. package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
  109. package/dist/oauth2/verify.d.mts +7 -13
  110. package/dist/oauth2/verify.mjs +2 -1
  111. package/dist/oauth2/verify.mjs.map +1 -0
  112. package/dist/social-providers/apple.d.mts +2 -1
  113. package/dist/social-providers/apple.mjs +22 -21
  114. package/dist/social-providers/apple.mjs.map +1 -0
  115. package/dist/social-providers/atlassian.d.mts +2 -1
  116. package/dist/social-providers/atlassian.mjs +2 -1
  117. package/dist/social-providers/atlassian.mjs.map +1 -0
  118. package/dist/social-providers/cognito.d.mts +2 -1
  119. package/dist/social-providers/cognito.mjs +4 -3
  120. package/dist/social-providers/cognito.mjs.map +1 -0
  121. package/dist/social-providers/discord.d.mts +2 -1
  122. package/dist/social-providers/discord.mjs +2 -1
  123. package/dist/social-providers/discord.mjs.map +1 -0
  124. package/dist/social-providers/dropbox.d.mts +2 -1
  125. package/dist/social-providers/dropbox.mjs +2 -1
  126. package/dist/social-providers/dropbox.mjs.map +1 -0
  127. package/dist/social-providers/facebook.d.mts +2 -1
  128. package/dist/social-providers/facebook.mjs +13 -12
  129. package/dist/social-providers/facebook.mjs.map +1 -0
  130. package/dist/social-providers/figma.d.mts +2 -1
  131. package/dist/social-providers/figma.mjs +2 -1
  132. package/dist/social-providers/figma.mjs.map +1 -0
  133. package/dist/social-providers/github.d.mts +3 -2
  134. package/dist/social-providers/github.mjs +23 -6
  135. package/dist/social-providers/github.mjs.map +1 -0
  136. package/dist/social-providers/gitlab.d.mts +2 -1
  137. package/dist/social-providers/gitlab.mjs +3 -2
  138. package/dist/social-providers/gitlab.mjs.map +1 -0
  139. package/dist/social-providers/google.d.mts +2 -1
  140. package/dist/social-providers/google.mjs +18 -13
  141. package/dist/social-providers/google.mjs.map +1 -0
  142. package/dist/social-providers/huggingface.d.mts +2 -1
  143. package/dist/social-providers/huggingface.mjs +3 -2
  144. package/dist/social-providers/huggingface.mjs.map +1 -0
  145. package/dist/social-providers/index.d.mts +61 -8
  146. package/dist/social-providers/index.mjs +5 -2
  147. package/dist/social-providers/index.mjs.map +1 -0
  148. package/dist/social-providers/kakao.d.mts +3 -2
  149. package/dist/social-providers/kakao.mjs +3 -2
  150. package/dist/social-providers/kakao.mjs.map +1 -0
  151. package/dist/social-providers/kick.d.mts +2 -1
  152. package/dist/social-providers/kick.mjs +2 -1
  153. package/dist/social-providers/kick.mjs.map +1 -0
  154. package/dist/social-providers/line.d.mts +2 -1
  155. package/dist/social-providers/line.mjs +3 -2
  156. package/dist/social-providers/line.mjs.map +1 -0
  157. package/dist/social-providers/linear.d.mts +2 -1
  158. package/dist/social-providers/linear.mjs +2 -1
  159. package/dist/social-providers/linear.mjs.map +1 -0
  160. package/dist/social-providers/linkedin.d.mts +2 -1
  161. package/dist/social-providers/linkedin.mjs +2 -1
  162. package/dist/social-providers/linkedin.mjs.map +1 -0
  163. package/dist/social-providers/microsoft-entra-id.d.mts +4 -1
  164. package/dist/social-providers/microsoft-entra-id.mjs +36 -2
  165. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
  166. package/dist/social-providers/naver.d.mts +11 -20
  167. package/dist/social-providers/naver.mjs +3 -2
  168. package/dist/social-providers/naver.mjs.map +1 -0
  169. package/dist/social-providers/notion.d.mts +2 -1
  170. package/dist/social-providers/notion.mjs +3 -2
  171. package/dist/social-providers/notion.mjs.map +1 -0
  172. package/dist/social-providers/paybin.d.mts +2 -1
  173. package/dist/social-providers/paybin.mjs +3 -2
  174. package/dist/social-providers/paybin.mjs.map +1 -0
  175. package/dist/social-providers/paypal.d.mts +2 -1
  176. package/dist/social-providers/paypal.mjs +2 -1
  177. package/dist/social-providers/paypal.mjs.map +1 -0
  178. package/dist/social-providers/polar.d.mts +2 -1
  179. package/dist/social-providers/polar.mjs +3 -2
  180. package/dist/social-providers/polar.mjs.map +1 -0
  181. package/dist/social-providers/railway.d.mts +68 -0
  182. package/dist/social-providers/railway.mjs +78 -0
  183. package/dist/social-providers/railway.mjs.map +1 -0
  184. package/dist/social-providers/reddit.d.mts +2 -1
  185. package/dist/social-providers/reddit.mjs +2 -1
  186. package/dist/social-providers/reddit.mjs.map +1 -0
  187. package/dist/social-providers/roblox.d.mts +2 -1
  188. package/dist/social-providers/roblox.mjs +2 -1
  189. package/dist/social-providers/roblox.mjs.map +1 -0
  190. package/dist/social-providers/salesforce.d.mts +2 -1
  191. package/dist/social-providers/salesforce.mjs +2 -1
  192. package/dist/social-providers/salesforce.mjs.map +1 -0
  193. package/dist/social-providers/slack.d.mts +2 -1
  194. package/dist/social-providers/slack.mjs +2 -1
  195. package/dist/social-providers/slack.mjs.map +1 -0
  196. package/dist/social-providers/spotify.d.mts +2 -1
  197. package/dist/social-providers/spotify.mjs +2 -1
  198. package/dist/social-providers/spotify.mjs.map +1 -0
  199. package/dist/social-providers/tiktok.d.mts +3 -3
  200. package/dist/social-providers/tiktok.mjs +3 -2
  201. package/dist/social-providers/tiktok.mjs.map +1 -0
  202. package/dist/social-providers/twitch.d.mts +2 -1
  203. package/dist/social-providers/twitch.mjs +2 -1
  204. package/dist/social-providers/twitch.mjs.map +1 -0
  205. package/dist/social-providers/twitter.d.mts +14 -25
  206. package/dist/social-providers/twitter.mjs +2 -1
  207. package/dist/social-providers/twitter.mjs.map +1 -0
  208. package/dist/social-providers/vercel.d.mts +2 -1
  209. package/dist/social-providers/vercel.mjs +3 -2
  210. package/dist/social-providers/vercel.mjs.map +1 -0
  211. package/dist/social-providers/vk.d.mts +2 -1
  212. package/dist/social-providers/vk.mjs +2 -1
  213. package/dist/social-providers/vk.mjs.map +1 -0
  214. package/dist/social-providers/zoom.d.mts +3 -10
  215. package/dist/social-providers/zoom.mjs +2 -1
  216. package/dist/social-providers/zoom.mjs.map +1 -0
  217. package/dist/types/context.d.mts +53 -21
  218. package/dist/types/cookie.d.mts +2 -1
  219. package/dist/types/helper.d.mts +4 -1
  220. package/dist/types/index.d.mts +4 -3
  221. package/dist/types/init-options.d.mts +231 -159
  222. package/dist/types/plugin-client.d.mts +4 -1
  223. package/dist/types/plugin.d.mts +12 -11
  224. package/dist/types/secret.d.mts +12 -0
  225. package/dist/utils/db.d.mts +12 -0
  226. package/dist/utils/db.mjs +17 -0
  227. package/dist/utils/db.mjs.map +1 -0
  228. package/dist/utils/deprecate.d.mts +2 -2
  229. package/dist/utils/deprecate.mjs +2 -1
  230. package/dist/utils/deprecate.mjs.map +1 -0
  231. package/dist/utils/error-codes.d.mts +8 -6
  232. package/dist/utils/error-codes.mjs +3 -2
  233. package/dist/utils/error-codes.mjs.map +1 -0
  234. package/dist/utils/id.d.mts +2 -1
  235. package/dist/utils/id.mjs +2 -1
  236. package/dist/utils/id.mjs.map +1 -0
  237. package/dist/utils/ip.d.mts +2 -1
  238. package/dist/utils/ip.mjs +2 -1
  239. package/dist/utils/ip.mjs.map +1 -0
  240. package/dist/utils/json.d.mts +2 -1
  241. package/dist/utils/json.mjs +2 -1
  242. package/dist/utils/json.mjs.map +1 -0
  243. package/dist/utils/string.d.mts +2 -1
  244. package/dist/utils/string.mjs +2 -1
  245. package/dist/utils/string.mjs.map +1 -0
  246. package/dist/utils/url.d.mts +2 -1
  247. package/dist/utils/url.mjs +2 -1
  248. package/dist/utils/url.mjs.map +1 -0
  249. package/package.json +35 -13
  250. package/src/db/adapter/factory.ts +41 -73
  251. package/src/db/adapter/get-id-field.ts +1 -3
  252. package/src/db/adapter/index.ts +20 -15
  253. package/src/db/adapter/types.ts +2 -41
  254. package/src/db/get-tables.ts +48 -37
  255. package/src/db/index.ts +30 -5
  256. package/src/db/schema/account.ts +16 -3
  257. package/src/db/schema/rate-limit.ts +16 -1
  258. package/src/db/schema/session.ts +15 -3
  259. package/src/db/schema/user.ts +15 -3
  260. package/src/db/schema/verification.ts +16 -3
  261. package/src/db/test/get-tables.test.ts +33 -0
  262. package/src/db/type.ts +154 -1
  263. package/src/env/env-impl.ts +2 -2
  264. package/src/env/logger.ts +1 -1
  265. package/src/error/codes.ts +17 -0
  266. package/src/oauth2/client-credentials-token.ts +26 -2
  267. package/src/oauth2/create-authorization-url.ts +3 -1
  268. package/src/oauth2/index.ts +3 -0
  269. package/src/oauth2/oauth-provider.ts +1 -1
  270. package/src/oauth2/refresh-access-token.test.ts +90 -0
  271. package/src/oauth2/refresh-access-token.ts +37 -4
  272. package/src/oauth2/validate-authorization-code.ts +55 -24
  273. package/src/oauth2/validate-token.test.ts +107 -52
  274. package/src/social-providers/apple.ts +29 -29
  275. package/src/social-providers/cognito.ts +6 -5
  276. package/src/social-providers/facebook.ts +3 -3
  277. package/src/social-providers/github.ts +26 -4
  278. package/src/social-providers/gitlab.ts +1 -1
  279. package/src/social-providers/google.ts +18 -14
  280. package/src/social-providers/huggingface.ts +1 -1
  281. package/src/social-providers/index.ts +9 -5
  282. package/src/social-providers/kakao.ts +1 -1
  283. package/src/social-providers/line.ts +1 -1
  284. package/src/social-providers/microsoft-entra-id.ts +84 -1
  285. package/src/social-providers/naver.ts +1 -1
  286. package/src/social-providers/notion.ts +1 -1
  287. package/src/social-providers/paybin.ts +1 -5
  288. package/src/social-providers/polar.ts +1 -1
  289. package/src/social-providers/railway.ts +100 -0
  290. package/src/social-providers/tiktok.ts +2 -1
  291. package/src/social-providers/vercel.ts +1 -1
  292. package/src/social-providers/zoom.ts +0 -8
  293. package/src/types/context.ts +74 -14
  294. package/src/types/helper.ts +9 -0
  295. package/src/types/index.ts +14 -2
  296. package/src/types/init-options.ts +294 -186
  297. package/src/types/plugin-client.ts +1 -0
  298. package/src/types/plugin.ts +11 -6
  299. package/src/types/secret.ts +8 -0
  300. package/src/utils/db.ts +20 -0
  301. package/src/utils/deprecate.test.ts +0 -1
  302. package/src/utils/error-codes.ts +12 -9
  303. package/.turbo/turbo-build.log +0 -182
  304. package/tsconfig.json +0 -7
  305. package/tsdown.config.ts +0 -32
  306. package/vitest.config.ts +0 -3
@@ -8,17 +8,28 @@ export type {
8
8
  InternalAdapter,
9
9
  PluginContext,
10
10
  } from "./context";
11
- export type { BetterAuthCookie, BetterAuthCookies } from "./cookie";
11
+ export type {
12
+ BetterAuthCookie,
13
+ BetterAuthCookies,
14
+ } from "./cookie";
12
15
  export type * from "./helper";
13
16
  export type {
17
+ BaseURLConfig,
14
18
  BetterAuthAdvancedOptions,
19
+ BetterAuthDBOptions,
15
20
  BetterAuthOptions,
16
21
  BetterAuthRateLimitOptions,
17
22
  BetterAuthRateLimitRule,
18
23
  BetterAuthRateLimitStorage,
24
+ DynamicBaseURLConfig,
19
25
  GenerateIdFn,
26
+ StoreIdentifierOption,
20
27
  } from "./init-options";
21
- export type { BetterAuthPlugin, HookEndpointContext } from "./plugin";
28
+ export type {
29
+ BetterAuthPlugin,
30
+ BetterAuthPluginErrorCodePart,
31
+ HookEndpointContext,
32
+ } from "./plugin";
22
33
  export type {
23
34
  BetterAuthClientOptions,
24
35
  BetterAuthClientPlugin,
@@ -26,3 +37,4 @@ export type {
26
37
  ClientFetchOption,
27
38
  ClientStore,
28
39
  } from "./plugin-client";
40
+ export type { SecretConfig } from "./secret";
@@ -1,5 +1,6 @@
1
1
  import type { Database as BunDatabase } from "bun:sqlite";
2
2
  import type { DatabaseSync } from "node:sqlite";
3
+ import type { D1Database } from "@cloudflare/workers-types";
3
4
  import type { CookieOptions } from "better-call";
4
5
  import type {
5
6
  Dialect,
@@ -20,23 +21,79 @@ import type {
20
21
  Verification,
21
22
  } from "../db";
22
23
  import type { DBAdapterDebugLogOption, DBAdapterInstance } from "../db/adapter";
24
+ import type { BaseAccount } from "../db/schema/account";
25
+ import type { BaseRateLimit } from "../db/schema/rate-limit";
26
+ import type { BaseSession } from "../db/schema/session";
27
+ import type { BaseUser } from "../db/schema/user";
28
+ import type { BaseVerification } from "../db/schema/verification";
23
29
  import type { Logger } from "../env";
24
30
  import type { SocialProviderList, SocialProviders } from "../social-providers";
25
31
  import type { AuthContext, GenericEndpointContext } from "./context";
26
- import type { Awaitable, LiteralUnion } from "./helper";
32
+ import type { Awaitable, LiteralString, LiteralUnion } from "./helper";
27
33
  import type { BetterAuthPlugin } from "./plugin";
28
34
 
29
35
  type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql";
30
- type OmitId<T extends { id: unknown }> = Omit<T, "id">;
31
36
  type Optional<T> = {
32
37
  [P in keyof T]?: T[P] | undefined;
33
38
  };
34
39
 
40
+ export type StoreIdentifierOption =
41
+ | "plain"
42
+ | "hashed"
43
+ | { hash: (identifier: string) => Promise<string> };
44
+
35
45
  export type GenerateIdFn = (options: {
36
46
  model: ModelNames;
37
47
  size?: number | undefined;
38
48
  }) => string | false;
39
49
 
50
+ /**
51
+ * Configuration for dynamic base URL resolution.
52
+ * Allows Better Auth to work with multiple domains (e.g., Vercel preview deployments).
53
+ */
54
+ export type DynamicBaseURLConfig = {
55
+ /**
56
+ * List of allowed hostnames. Supports wildcard patterns.
57
+ *
58
+ * The derived host from the request will be validated against this list.
59
+ * Uses the same wildcard matching as `trustedOrigins`.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * allowedHosts: [
64
+ * "myapp.com", // Exact match
65
+ * "*.vercel.app", // Any Vercel preview
66
+ * "preview-*.myapp.com" // Pattern match
67
+ * ]
68
+ * ```
69
+ */
70
+ allowedHosts: string[];
71
+
72
+ /**
73
+ * Fallback URL to use if the derived host doesn't match any allowed host.
74
+ * If not set, Better Auth will throw an error when the host doesn't match.
75
+ *
76
+ * @example "https://myapp.com"
77
+ */
78
+ fallback?: string | undefined;
79
+
80
+ /**
81
+ * Protocol to use when constructing the URL.
82
+ * - `"https"`: Always use HTTPS (recommended for production)
83
+ * - `"http"`: Always use HTTP (for local development)
84
+ * - `"auto"`: Derive from `x-forwarded-proto` header or default to HTTPS
85
+ *
86
+ * @default "auto"
87
+ */
88
+ protocol?: "http" | "https" | "auto" | undefined;
89
+ };
90
+
91
+ /**
92
+ * Base URL configuration.
93
+ * Can be a static string or a dynamic config for multi-domain deployments.
94
+ */
95
+ export type BaseURLConfig = string | DynamicBaseURLConfig;
96
+
40
97
  export interface BetterAuthRateLimitStorage {
41
98
  get: (key: string) => Promise<RateLimit | null | undefined>;
42
99
  set: (
@@ -62,57 +119,70 @@ export type BetterAuthRateLimitRule = {
62
119
  max: number;
63
120
  };
64
121
 
65
- export type BetterAuthRateLimitOptions = Optional<BetterAuthRateLimitRule> & {
122
+ export type BetterAuthDBOptions<
123
+ ModelName extends string,
124
+ Keys extends string = string,
125
+ > = {
66
126
  /**
67
- * By default, rate limiting is only
68
- * enabled on production.
127
+ * The name of the model. Defaults to the model name.
69
128
  */
70
- enabled?: boolean | undefined;
129
+ modelName?: ModelName | LiteralString;
71
130
  /**
72
- * Custom rate limit rules to apply to
73
- * specific paths.
131
+ * Map fields to database columns
74
132
  */
75
- customRules?:
76
- | {
77
- [key: string]:
78
- | BetterAuthRateLimitRule
79
- | false
80
- | ((
81
- request: Request,
82
- currentRule: BetterAuthRateLimitRule,
83
- ) => Awaitable<false | BetterAuthRateLimitRule>);
84
- }
85
- | undefined;
86
- /**
87
- * Storage configuration
88
- *
89
- * By default, rate limiting is stored in memory. If you passed a
90
- * secondary storage, rate limiting will be stored in the secondary
91
- * storage.
92
- *
93
- * @default "memory"
94
- */
95
- storage?: ("memory" | "database" | "secondary-storage") | undefined;
96
- /**
97
- * If database is used as storage, the name of the table to
98
- * use for rate limiting.
99
- *
100
- * @default "rateLimit"
101
- */
102
- modelName?: string | undefined;
103
- /**
104
- * Custom field names for the rate limit table
105
- */
106
- fields?: Partial<Record<keyof RateLimit, string>> | undefined;
133
+ fields?: Partial<Record<Exclude<Keys, "id">, string>>;
107
134
  /**
108
- * custom storage configuration.
109
- *
110
- * NOTE: If custom storage is used storage
111
- * is ignored
135
+ * Additional fields for the model
112
136
  */
113
- customStorage?: BetterAuthRateLimitStorage;
137
+ additionalFields?: {
138
+ [Key in Exclude<string, Keys | "id">]: DBFieldAttribute;
139
+ };
114
140
  };
115
141
 
142
+ export type BetterAuthRateLimitOptions = Optional<BetterAuthRateLimitRule> &
143
+ Omit<
144
+ BetterAuthDBOptions<"rateLimit", keyof BaseRateLimit>,
145
+ "additionalFields"
146
+ > & {
147
+ /**
148
+ * By default, rate limiting is only
149
+ * enabled on production.
150
+ */
151
+ enabled?: boolean | undefined;
152
+ /**
153
+ * Custom rate limit rules to apply to
154
+ * specific paths.
155
+ */
156
+ customRules?:
157
+ | {
158
+ [key: string]:
159
+ | BetterAuthRateLimitRule
160
+ | false
161
+ | ((
162
+ request: Request,
163
+ currentRule: BetterAuthRateLimitRule,
164
+ ) => Awaitable<false | BetterAuthRateLimitRule>);
165
+ }
166
+ | undefined;
167
+ /**
168
+ * Storage configuration
169
+ *
170
+ * By default, rate limiting is stored in memory. If you passed a
171
+ * secondary storage, rate limiting will be stored in the secondary
172
+ * storage.
173
+ *
174
+ * @default "memory"
175
+ */
176
+ storage?: ("memory" | "database" | "secondary-storage") | undefined;
177
+ /**
178
+ * custom storage configuration.
179
+ *
180
+ * NOTE: If custom storage is used storage
181
+ * is ignored
182
+ */
183
+ customStorage?: BetterAuthRateLimitStorage;
184
+ };
185
+
116
186
  export type BetterAuthAdvancedOptions = {
117
187
  /**
118
188
  * Ip address configuration
@@ -138,23 +208,11 @@ export type BetterAuthAdvancedOptions = {
138
208
  disableIpTracking?: boolean;
139
209
  /**
140
210
  * IPv6 subnet prefix length for rate limiting.
211
+ * IPv6 addresses will be normalized to this subnet.
141
212
  *
142
- * IPv6 addresses can be grouped by subnet to prevent attackers from
143
- * bypassing rate limits by rotating through multiple addresses in
144
- * their allocation.
145
- *
146
- * Common values:
147
- * - 128 (default): Individual IPv6 address
148
- * - 64: /64 subnet (typical home/business allocation)
149
- * - 48: /48 subnet (larger network allocation)
150
- * - 32: /32 subnet (ISP allocation)
151
- *
152
- * Note: This only affects IPv6 addresses. IPv4 addresses are always
153
- * rate limited individually.
154
- *
155
- * @default 64 (/64 subnet)
213
+ * @default 64
156
214
  */
157
- ipv6Subnet?: 128 | 64 | 48 | 32 | undefined;
215
+ ipv6Subnet?: 128 | 64 | 48 | 32;
158
216
  }
159
217
  | undefined;
160
218
  /**
@@ -256,17 +314,6 @@ export type BetterAuthAdvancedOptions = {
256
314
  * @default 100
257
315
  */
258
316
  defaultFindManyLimit?: number;
259
- /**
260
- * If your database auto increments number ids, set this to `true`.
261
- *
262
- * Note: If enabled, we will not handle ID generation (including if you use `generateId`), and it would be expected that your database will provide the ID automatically.
263
- *
264
- * @default false
265
- *
266
- * @deprecated Please use `generateId` instead. This will be removed in future
267
- * releases.
268
- */
269
- useNumberId?: boolean;
270
317
  /**
271
318
  * Custom generateId function.
272
319
  *
@@ -322,14 +369,17 @@ export type BetterAuthAdvancedOptions = {
322
369
  * }
323
370
  */
324
371
  backgroundTasks?: {
325
- handler: (promise: Promise<void>) => void;
372
+ handler: (promise: Promise<unknown>) => void;
326
373
  };
327
374
  /**
328
- * Skip trailing slash validation in route matching
375
+ * Skip trailing slashes in API routes.
376
+ *
377
+ * When enabled, requests with trailing slashes (e.g., `/api/auth/session/`)
378
+ * will be handled the same as requests without (e.g., `/api/auth/session`).
329
379
  *
330
380
  * @default false
331
381
  */
332
- skipTrailingSlashes?: boolean | undefined;
382
+ skipTrailingSlashes?: boolean;
333
383
  };
334
384
 
335
385
  export type BetterAuthOptions = {
@@ -344,12 +394,27 @@ export type BetterAuthOptions = {
344
394
  /**
345
395
  * Base URL for the Better Auth. This is typically the
346
396
  * root URL where your application server is hosted.
347
- * If not explicitly set,
348
- * the system will check the following environment variable:
349
397
  *
350
- * process.env.BETTER_AUTH_URL
398
+ * Can be configured as:
399
+ * - A static string: `"https://myapp.com"`
400
+ * - A dynamic config with allowed hosts for multi-domain deployments
401
+ *
402
+ * If not explicitly set, the system will check environment variables:
403
+ * `BETTER_AUTH_URL`, `NEXT_PUBLIC_BETTER_AUTH_URL`, etc.
404
+ *
405
+ * @example
406
+ * ```ts
407
+ * // Static URL
408
+ * baseURL: "https://myapp.com"
409
+ *
410
+ * // Dynamic with allowed hosts (for Vercel, multi-domain, etc.)
411
+ * baseURL: {
412
+ * allowedHosts: ["myapp.com", "*.vercel.app", "preview-*.myapp.com"],
413
+ * fallback: "https://myapp.com"
414
+ * }
415
+ * ```
351
416
  */
352
- baseURL?: string | undefined;
417
+ baseURL?: BaseURLConfig | undefined;
353
418
  /**
354
419
  * Base path for the Better Auth. This is typically
355
420
  * the path where the
@@ -382,6 +447,19 @@ export type BetterAuthOptions = {
382
447
  * ```
383
448
  */
384
449
  secret?: string | undefined;
450
+ /**
451
+ * Versioned secrets for non-destructive secret rotation.
452
+ * When set, encryption uses an envelope format with key IDs.
453
+ * First entry is the current key used for new encryption.
454
+ * Remaining entries are decryption-only (previous rotations).
455
+ *
456
+ * Can also be set via BETTER_AUTH_SECRETS env var:
457
+ * `BETTER_AUTH_SECRETS=2:base64secret,1:base64secret`
458
+ *
459
+ * When set, `secret` is only used as legacy fallback
460
+ * for decrypting bare-hex payloads that predate the envelope format.
461
+ */
462
+ secrets?: Array<{ version: number; value: string }> | undefined;
385
463
  /**
386
464
  * Database configuration
387
465
  */
@@ -394,6 +472,7 @@ export type BetterAuthOptions = {
394
472
  | DBAdapterInstance
395
473
  | BunDatabase
396
474
  | DatabaseSync
475
+ | D1Database
397
476
  | {
398
477
  dialect: Dialect;
399
478
  type: KyselyDatabaseType;
@@ -485,10 +564,13 @@ export type BetterAuthOptions = {
485
564
  request?: Request,
486
565
  ) => Promise<void>;
487
566
  /**
488
- * Send a verification email automatically
489
- * after sign up
567
+ * Send a verification email automatically after sign up.
490
568
  *
491
- * @default false
569
+ * - `true`: Always send verification email on sign up
570
+ * - `false`: Never send verification email on sign up
571
+ * - `undefined`: Follows `requireEmailVerification` behavior
572
+ *
573
+ * @default undefined
492
574
  */
493
575
  sendOnSignUp?: boolean;
494
576
  /**
@@ -508,13 +590,6 @@ export type BetterAuthOptions = {
508
590
  * @default 3600 seconds (1 hour)
509
591
  */
510
592
  expiresIn?: number;
511
- /**
512
- * A function that is called when a user verifies their email
513
- * @param user the user that verified their email
514
- * @param request the request object
515
- * @deprecated Use `beforeEmailVerification` or `afterEmailVerification` instead. This will be removed in 1.5
516
- */
517
- onEmailVerification?: (user: User, request?: Request) => Promise<void>;
518
593
  /**
519
594
  * A function that is called before a user verifies their email
520
595
  * @param user the user that verified their email
@@ -629,6 +704,59 @@ export type BetterAuthOptions = {
629
704
  * @default false
630
705
  */
631
706
  revokeSessionsOnPasswordReset?: boolean;
707
+ /**
708
+ * A callback function that is triggered when a user tries to sign up
709
+ * with an email that already exists. Useful for notifying the existing user
710
+ * that someone attempted to register with their email.
711
+ *
712
+ * This is only called when `requireEmailVerification: true` or `autoSignIn: false`.
713
+ */
714
+ onExistingUserSignUp?: (
715
+ /**
716
+ * @param user the existing user from the database
717
+ */
718
+ data: { user: User },
719
+ request?: Request,
720
+ ) => Promise<void>;
721
+ /**
722
+ * Build a custom synthetic user for email enumeration
723
+ * protection. When a sign-up attempt is made with an
724
+ * email that already exists, this function is called
725
+ * to build the fake user response.
726
+ *
727
+ * Use this when plugins add fields to the user table
728
+ * (e.g. admin plugin adds `role`, `banned`, etc.)
729
+ * to ensure the fake response is indistinguishable
730
+ * from a real sign-up.
731
+ *
732
+ * @example
733
+ * ```ts
734
+ * customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
735
+ * ...coreFields,
736
+ * role: "user",
737
+ * banned: false,
738
+ * banReason: null,
739
+ * banExpires: null,
740
+ * ...additionalFields,
741
+ * id,
742
+ * })
743
+ * ```
744
+ */
745
+ customSyntheticUser?: (params: {
746
+ /** Core user fields: name, email, emailVerified, image, createdAt, updatedAt */
747
+ coreFields: {
748
+ name: string;
749
+ email: string;
750
+ emailVerified: boolean;
751
+ image: string | null;
752
+ createdAt: Date;
753
+ updatedAt: Date;
754
+ };
755
+ /** Processed additional fields from options.user.additionalFields (with defaults applied) */
756
+ additionalFields: Record<string, unknown>;
757
+ /** Generated user ID */
758
+ id: string;
759
+ }) => Record<string, unknown>;
632
760
  }
633
761
  | undefined;
634
762
  /**
@@ -643,28 +771,7 @@ export type BetterAuthOptions = {
643
771
  * User configuration
644
772
  */
645
773
  user?:
646
- | {
647
- /**
648
- * The model name for the user. Defaults to "user".
649
- */
650
- modelName?: string;
651
- /**
652
- * Map fields
653
- *
654
- * @example
655
- * ```ts
656
- * {
657
- * userId: "user_id"
658
- * }
659
- * ```
660
- */
661
- fields?: Partial<Record<keyof OmitId<User>, string>>;
662
- /**
663
- * Additional fields for the user
664
- */
665
- additionalFields?: {
666
- [key: string]: DBFieldAttribute;
667
- };
774
+ | (BetterAuthDBOptions<"user", keyof BaseUser> & {
668
775
  /**
669
776
  * Changing email configuration
670
777
  */
@@ -674,21 +781,6 @@ export type BetterAuthOptions = {
674
781
  * @default false
675
782
  */
676
783
  enabled: boolean;
677
- /**
678
- * Send a verification email when the user changes their email.
679
- * @param data the data object
680
- * @param request the request object
681
- * @deprecated Use `sendChangeEmailConfirmation` instead
682
- */
683
- sendChangeEmailVerification?: (
684
- data: {
685
- user: User;
686
- newEmail: string;
687
- url: string;
688
- token: string;
689
- },
690
- request?: Request,
691
- ) => Promise<void>;
692
784
  /**
693
785
  * Send a confirmation email to the old email address when the user changes their email.
694
786
  * @param data the data object
@@ -751,26 +843,10 @@ export type BetterAuthOptions = {
751
843
  */
752
844
  deleteTokenExpiresIn?: number;
753
845
  };
754
- }
846
+ })
755
847
  | undefined;
756
848
  session?:
757
- | {
758
- /**
759
- * The model name for the session.
760
- *
761
- * @default "session"
762
- */
763
- modelName?: string;
764
- /**
765
- * Map fields
766
- *
767
- * @example
768
- * ```ts
769
- * {
770
- * userId: "user_id"
771
- * }
772
- */
773
- fields?: Partial<Record<keyof OmitId<Session>, string>>;
849
+ | (BetterAuthDBOptions<"session", keyof BaseSession> & {
774
850
  /**
775
851
  * Expiration time for the session token. The value
776
852
  * should be in seconds.
@@ -792,11 +868,13 @@ export type BetterAuthOptions = {
792
868
  */
793
869
  disableSessionRefresh?: boolean;
794
870
  /**
795
- * Additional fields for the session
871
+ * Defer session refresh writes to POST requests.
872
+ * When enabled, GET is read-only and POST performs refresh.
873
+ * Useful for read-replica database setups.
874
+ *
875
+ * @default false
796
876
  */
797
- additionalFields?: {
798
- [key: string]: DBFieldAttribute;
799
- };
877
+ deferSessionRefresh?: boolean;
800
878
  /**
801
879
  * By default if secondary storage is provided
802
880
  * the session is stored in the secondary storage.
@@ -906,24 +984,10 @@ export type BetterAuthOptions = {
906
984
  * @default 1 day (60 * 60 * 24)
907
985
  */
908
986
  freshAge?: number;
909
- }
987
+ })
910
988
  | undefined;
911
989
  account?:
912
- | {
913
- /**
914
- * The model name for the account. Defaults to "account".
915
- */
916
- modelName?: string;
917
- /**
918
- * Map fields
919
- */
920
- fields?: Partial<Record<keyof OmitId<Account>, string>>;
921
- /**
922
- * Additional fields for the account
923
- */
924
- additionalFields?: {
925
- [key: string]: DBFieldAttribute;
926
- };
990
+ | (BetterAuthDBOptions<"account", keyof BaseAccount> & {
927
991
  /**
928
992
  * When enabled (true), the user account data (accessToken, idToken, refreshToken, etc.)
929
993
  * will be updated on sign in with the latest data from the provider.
@@ -942,11 +1006,54 @@ export type BetterAuthOptions = {
942
1006
  */
943
1007
  enabled?: boolean;
944
1008
  /**
945
- * List of trusted providers
1009
+ * Disable implicit account linking on sign-in.
1010
+ *
1011
+ * When enabled, accounts will not be automatically linked
1012
+ * during OAuth sign-in, even if the email is verified or
1013
+ * the provider is trusted. Users must explicitly link
1014
+ * accounts using `linkSocial()` while authenticated.
1015
+ *
1016
+ * @default false
1017
+ */
1018
+ disableImplicitLinking?: boolean;
1019
+ /**
1020
+ * List of trusted providers. Can be a static array or a function
1021
+ * that returns providers dynamically. The function is called
1022
+ * during context init (with `request` undefined) and again
1023
+ * on each request (with the incoming Request). It must be
1024
+ * resilient to `request` being undefined.
1025
+ *
1026
+ * @example
1027
+ * ```ts
1028
+ * trustedProviders: ["google", "github"]
1029
+ * ```
1030
+ *
1031
+ * @example
1032
+ * ```ts
1033
+ * trustedProviders: async (request) => {
1034
+ * if (!request) return [];
1035
+ * const providers = await getTrustedProvidersForTenant(request);
1036
+ * return providers;
1037
+ * }
1038
+ * ```
946
1039
  */
947
- trustedProviders?: Array<
948
- LiteralUnion<SocialProviderList[number] | "email-password", string>
949
- >;
1040
+ trustedProviders?:
1041
+ | Array<
1042
+ LiteralUnion<
1043
+ SocialProviderList[number] | "email-password",
1044
+ string
1045
+ >
1046
+ >
1047
+ | ((
1048
+ request?: Request | undefined,
1049
+ ) => Awaitable<
1050
+ Array<
1051
+ LiteralUnion<
1052
+ SocialProviderList[number] | "email-password",
1053
+ string
1054
+ >
1055
+ >
1056
+ >);
950
1057
  /**
951
1058
  * If enabled (true), this will allow users to manually linking accounts with different email addresses than the main user.
952
1059
  *
@@ -1008,33 +1115,34 @@ export type BetterAuthOptions = {
1008
1115
  * @note This is automatically set to true if you haven't passed a database
1009
1116
  */
1010
1117
  storeAccountCookie?: boolean;
1011
- }
1118
+ })
1012
1119
  | undefined;
1013
- /**
1014
- * Verification configuration
1015
- */
1016
1120
  verification?:
1017
- | {
1121
+ | (BetterAuthDBOptions<"verification", keyof BaseVerification> & {
1018
1122
  /**
1019
- * Change the modelName of the verification table
1020
- */
1021
- modelName?: string;
1022
- /**
1023
- * Map verification fields
1123
+ * disable cleaning up expired values when a verification value is
1124
+ * fetched
1024
1125
  */
1025
- fields?: Partial<Record<keyof OmitId<Verification>, string>>;
1126
+ disableCleanup?: boolean;
1026
1127
  /**
1027
- * Additional fields for the verification
1128
+ * How to store verification identifiers (tokens, OTPs, etc.)
1129
+ *
1130
+ * @example "hashed"
1131
+ *
1132
+ * @default "plain"
1028
1133
  */
1029
- additionalFields?: {
1030
- [key: string]: DBFieldAttribute;
1031
- };
1134
+ storeIdentifier?:
1135
+ | StoreIdentifierOption
1136
+ | {
1137
+ default: StoreIdentifierOption;
1138
+ overrides?: Record<string, StoreIdentifierOption>;
1139
+ };
1032
1140
  /**
1033
- * disable cleaning up expired values when a verification value is
1034
- * fetched
1141
+ * Store verification data in database even when secondary storage is configured.
1142
+ * @default false
1035
1143
  */
1036
- disableCleanup?: boolean;
1037
- }
1144
+ storeInDatabase?: boolean;
1145
+ })
1038
1146
  | undefined;
1039
1147
  /**
1040
1148
  * List of trusted origins.
@@ -17,6 +17,7 @@ export interface ClientStore {
17
17
  export type ClientAtomListener = {
18
18
  matcher: (path: string) => boolean;
19
19
  signal: "$sessionSignal" | Omit<string, "$sessionSignal">;
20
+ callback?: (path: string) => void;
20
21
  };
21
22
 
22
23
  /**