@better-auth/core 1.5.0-beta.8 → 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 (313) 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/index.d.mts +2 -2
  18. package/dist/context/index.mjs +2 -2
  19. package/dist/context/request-state.d.mts +2 -1
  20. package/dist/context/request-state.mjs +4 -3
  21. package/dist/context/request-state.mjs.map +1 -0
  22. package/dist/context/transaction.d.mts +12 -3
  23. package/dist/context/transaction.mjs +55 -11
  24. package/dist/context/transaction.mjs.map +1 -0
  25. package/dist/db/adapter/factory.d.mts +6 -13
  26. package/dist/db/adapter/factory.mjs +44 -57
  27. package/dist/db/adapter/factory.mjs.map +1 -0
  28. package/dist/db/adapter/get-default-field-name.d.mts +2 -1
  29. package/dist/db/adapter/get-default-field-name.mjs +3 -2
  30. package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
  31. package/dist/db/adapter/get-default-model-name.d.mts +2 -1
  32. package/dist/db/adapter/get-default-model-name.mjs +5 -4
  33. package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
  34. package/dist/db/adapter/get-field-attributes.d.mts +3 -2
  35. package/dist/db/adapter/get-field-attributes.mjs +2 -1
  36. package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
  37. package/dist/db/adapter/get-field-name.d.mts +2 -1
  38. package/dist/db/adapter/get-field-name.mjs +2 -1
  39. package/dist/db/adapter/get-field-name.mjs.map +1 -0
  40. package/dist/db/adapter/get-id-field.d.mts +3 -2
  41. package/dist/db/adapter/get-id-field.mjs +3 -2
  42. package/dist/db/adapter/get-id-field.mjs.map +1 -0
  43. package/dist/db/adapter/get-model-name.d.mts +2 -1
  44. package/dist/db/adapter/get-model-name.mjs +2 -1
  45. package/dist/db/adapter/get-model-name.mjs.map +1 -0
  46. package/dist/db/adapter/index.d.mts +10 -4
  47. package/dist/db/adapter/index.mjs +19 -2
  48. package/dist/db/adapter/index.mjs.map +1 -0
  49. package/dist/db/adapter/types.d.mts +3 -34
  50. package/dist/db/adapter/utils.d.mts +2 -1
  51. package/dist/db/adapter/utils.mjs +2 -1
  52. package/dist/db/adapter/utils.mjs.map +1 -0
  53. package/dist/db/get-tables.d.mts +2 -1
  54. package/dist/db/get-tables.mjs +46 -39
  55. package/dist/db/get-tables.mjs.map +1 -0
  56. package/dist/db/index.d.mts +7 -7
  57. package/dist/db/plugin.d.mts +2 -1
  58. package/dist/db/schema/account.d.mts +8 -4
  59. package/dist/db/schema/account.mjs +2 -1
  60. package/dist/db/schema/account.mjs.map +1 -0
  61. package/dist/db/schema/rate-limit.d.mts +8 -2
  62. package/dist/db/schema/rate-limit.mjs +2 -1
  63. package/dist/db/schema/rate-limit.mjs.map +1 -0
  64. package/dist/db/schema/session.d.mts +8 -4
  65. package/dist/db/schema/session.mjs +2 -1
  66. package/dist/db/schema/session.mjs.map +1 -0
  67. package/dist/db/schema/shared.d.mts +2 -1
  68. package/dist/db/schema/shared.mjs +2 -1
  69. package/dist/db/schema/shared.mjs.map +1 -0
  70. package/dist/db/schema/user.d.mts +8 -4
  71. package/dist/db/schema/user.mjs +2 -1
  72. package/dist/db/schema/user.mjs.map +1 -0
  73. package/dist/db/schema/verification.d.mts +8 -4
  74. package/dist/db/schema/verification.mjs +2 -1
  75. package/dist/db/schema/verification.mjs.map +1 -0
  76. package/dist/db/type.d.mts +28 -2
  77. package/dist/env/color-depth.d.mts +2 -1
  78. package/dist/env/color-depth.mjs +2 -1
  79. package/dist/env/color-depth.mjs.map +1 -0
  80. package/dist/env/env-impl.d.mts +3 -2
  81. package/dist/env/env-impl.mjs +9 -8
  82. package/dist/env/env-impl.mjs.map +1 -0
  83. package/dist/env/logger.d.mts +2 -1
  84. package/dist/env/logger.mjs +3 -2
  85. package/dist/env/logger.mjs.map +1 -0
  86. package/dist/error/codes.d.mts +64 -181
  87. package/dist/error/codes.mjs +6 -2
  88. package/dist/error/codes.mjs.map +1 -0
  89. package/dist/error/index.d.mts +2 -1
  90. package/dist/error/index.mjs +2 -1
  91. package/dist/error/index.mjs.map +1 -0
  92. package/dist/index.d.mts +5 -4
  93. package/dist/oauth2/client-credentials-token.d.mts +25 -3
  94. package/dist/oauth2/client-credentials-token.mjs +15 -2
  95. package/dist/oauth2/client-credentials-token.mjs.map +1 -0
  96. package/dist/oauth2/create-authorization-url.d.mts +5 -2
  97. package/dist/oauth2/create-authorization-url.mjs +3 -1
  98. package/dist/oauth2/create-authorization-url.mjs.map +1 -0
  99. package/dist/oauth2/index.d.mts +4 -4
  100. package/dist/oauth2/index.mjs +4 -4
  101. package/dist/oauth2/oauth-provider.d.mts +3 -2
  102. package/dist/oauth2/refresh-access-token.d.mts +24 -4
  103. package/dist/oauth2/refresh-access-token.mjs +20 -2
  104. package/dist/oauth2/refresh-access-token.mjs.map +1 -0
  105. package/dist/oauth2/utils.d.mts +2 -1
  106. package/dist/oauth2/utils.mjs +2 -1
  107. package/dist/oauth2/utils.mjs.map +1 -0
  108. package/dist/oauth2/validate-authorization-code.d.mts +37 -4
  109. package/dist/oauth2/validate-authorization-code.mjs +25 -13
  110. package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
  111. package/dist/oauth2/verify.d.mts +7 -13
  112. package/dist/oauth2/verify.mjs +2 -1
  113. package/dist/oauth2/verify.mjs.map +1 -0
  114. package/dist/social-providers/apple.d.mts +2 -1
  115. package/dist/social-providers/apple.mjs +22 -21
  116. package/dist/social-providers/apple.mjs.map +1 -0
  117. package/dist/social-providers/atlassian.d.mts +2 -1
  118. package/dist/social-providers/atlassian.mjs +2 -1
  119. package/dist/social-providers/atlassian.mjs.map +1 -0
  120. package/dist/social-providers/cognito.d.mts +2 -1
  121. package/dist/social-providers/cognito.mjs +4 -3
  122. package/dist/social-providers/cognito.mjs.map +1 -0
  123. package/dist/social-providers/discord.d.mts +2 -1
  124. package/dist/social-providers/discord.mjs +2 -1
  125. package/dist/social-providers/discord.mjs.map +1 -0
  126. package/dist/social-providers/dropbox.d.mts +2 -1
  127. package/dist/social-providers/dropbox.mjs +3 -2
  128. package/dist/social-providers/dropbox.mjs.map +1 -0
  129. package/dist/social-providers/facebook.d.mts +2 -1
  130. package/dist/social-providers/facebook.mjs +13 -12
  131. package/dist/social-providers/facebook.mjs.map +1 -0
  132. package/dist/social-providers/figma.d.mts +2 -1
  133. package/dist/social-providers/figma.mjs +2 -1
  134. package/dist/social-providers/figma.mjs.map +1 -0
  135. package/dist/social-providers/github.d.mts +3 -2
  136. package/dist/social-providers/github.mjs +23 -6
  137. package/dist/social-providers/github.mjs.map +1 -0
  138. package/dist/social-providers/gitlab.d.mts +2 -1
  139. package/dist/social-providers/gitlab.mjs +3 -2
  140. package/dist/social-providers/gitlab.mjs.map +1 -0
  141. package/dist/social-providers/google.d.mts +2 -1
  142. package/dist/social-providers/google.mjs +18 -13
  143. package/dist/social-providers/google.mjs.map +1 -0
  144. package/dist/social-providers/huggingface.d.mts +2 -1
  145. package/dist/social-providers/huggingface.mjs +3 -2
  146. package/dist/social-providers/huggingface.mjs.map +1 -0
  147. package/dist/social-providers/index.d.mts +61 -8
  148. package/dist/social-providers/index.mjs +5 -2
  149. package/dist/social-providers/index.mjs.map +1 -0
  150. package/dist/social-providers/kakao.d.mts +3 -2
  151. package/dist/social-providers/kakao.mjs +3 -2
  152. package/dist/social-providers/kakao.mjs.map +1 -0
  153. package/dist/social-providers/kick.d.mts +2 -1
  154. package/dist/social-providers/kick.mjs +2 -1
  155. package/dist/social-providers/kick.mjs.map +1 -0
  156. package/dist/social-providers/line.d.mts +2 -1
  157. package/dist/social-providers/line.mjs +3 -2
  158. package/dist/social-providers/line.mjs.map +1 -0
  159. package/dist/social-providers/linear.d.mts +2 -1
  160. package/dist/social-providers/linear.mjs +2 -1
  161. package/dist/social-providers/linear.mjs.map +1 -0
  162. package/dist/social-providers/linkedin.d.mts +2 -1
  163. package/dist/social-providers/linkedin.mjs +2 -1
  164. package/dist/social-providers/linkedin.mjs.map +1 -0
  165. package/dist/social-providers/microsoft-entra-id.d.mts +4 -1
  166. package/dist/social-providers/microsoft-entra-id.mjs +36 -2
  167. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
  168. package/dist/social-providers/naver.d.mts +11 -20
  169. package/dist/social-providers/naver.mjs +3 -2
  170. package/dist/social-providers/naver.mjs.map +1 -0
  171. package/dist/social-providers/notion.d.mts +2 -1
  172. package/dist/social-providers/notion.mjs +3 -2
  173. package/dist/social-providers/notion.mjs.map +1 -0
  174. package/dist/social-providers/paybin.d.mts +2 -1
  175. package/dist/social-providers/paybin.mjs +3 -2
  176. package/dist/social-providers/paybin.mjs.map +1 -0
  177. package/dist/social-providers/paypal.d.mts +2 -1
  178. package/dist/social-providers/paypal.mjs +2 -1
  179. package/dist/social-providers/paypal.mjs.map +1 -0
  180. package/dist/social-providers/polar.d.mts +2 -1
  181. package/dist/social-providers/polar.mjs +3 -2
  182. package/dist/social-providers/polar.mjs.map +1 -0
  183. package/dist/social-providers/railway.d.mts +68 -0
  184. package/dist/social-providers/railway.mjs +78 -0
  185. package/dist/social-providers/railway.mjs.map +1 -0
  186. package/dist/social-providers/reddit.d.mts +2 -1
  187. package/dist/social-providers/reddit.mjs +2 -1
  188. package/dist/social-providers/reddit.mjs.map +1 -0
  189. package/dist/social-providers/roblox.d.mts +2 -1
  190. package/dist/social-providers/roblox.mjs +2 -1
  191. package/dist/social-providers/roblox.mjs.map +1 -0
  192. package/dist/social-providers/salesforce.d.mts +2 -1
  193. package/dist/social-providers/salesforce.mjs +2 -1
  194. package/dist/social-providers/salesforce.mjs.map +1 -0
  195. package/dist/social-providers/slack.d.mts +2 -1
  196. package/dist/social-providers/slack.mjs +2 -1
  197. package/dist/social-providers/slack.mjs.map +1 -0
  198. package/dist/social-providers/spotify.d.mts +2 -1
  199. package/dist/social-providers/spotify.mjs +2 -1
  200. package/dist/social-providers/spotify.mjs.map +1 -0
  201. package/dist/social-providers/tiktok.d.mts +3 -3
  202. package/dist/social-providers/tiktok.mjs +3 -2
  203. package/dist/social-providers/tiktok.mjs.map +1 -0
  204. package/dist/social-providers/twitch.d.mts +2 -1
  205. package/dist/social-providers/twitch.mjs +2 -1
  206. package/dist/social-providers/twitch.mjs.map +1 -0
  207. package/dist/social-providers/twitter.d.mts +14 -25
  208. package/dist/social-providers/twitter.mjs +2 -1
  209. package/dist/social-providers/twitter.mjs.map +1 -0
  210. package/dist/social-providers/vercel.d.mts +2 -1
  211. package/dist/social-providers/vercel.mjs +3 -2
  212. package/dist/social-providers/vercel.mjs.map +1 -0
  213. package/dist/social-providers/vk.d.mts +2 -1
  214. package/dist/social-providers/vk.mjs +2 -1
  215. package/dist/social-providers/vk.mjs.map +1 -0
  216. package/dist/social-providers/zoom.d.mts +3 -10
  217. package/dist/social-providers/zoom.mjs +2 -1
  218. package/dist/social-providers/zoom.mjs.map +1 -0
  219. package/dist/types/context.d.mts +54 -21
  220. package/dist/types/cookie.d.mts +2 -1
  221. package/dist/types/helper.d.mts +4 -1
  222. package/dist/types/index.d.mts +4 -3
  223. package/dist/types/init-options.d.mts +235 -144
  224. package/dist/types/plugin-client.d.mts +4 -1
  225. package/dist/types/plugin.d.mts +12 -11
  226. package/dist/types/secret.d.mts +12 -0
  227. package/dist/utils/db.d.mts +12 -0
  228. package/dist/utils/db.mjs +17 -0
  229. package/dist/utils/db.mjs.map +1 -0
  230. package/dist/utils/deprecate.d.mts +2 -2
  231. package/dist/utils/deprecate.mjs +2 -1
  232. package/dist/utils/deprecate.mjs.map +1 -0
  233. package/dist/utils/error-codes.d.mts +8 -6
  234. package/dist/utils/error-codes.mjs +3 -2
  235. package/dist/utils/error-codes.mjs.map +1 -0
  236. package/dist/utils/id.d.mts +2 -1
  237. package/dist/utils/id.mjs +2 -1
  238. package/dist/utils/id.mjs.map +1 -0
  239. package/dist/utils/ip.d.mts +55 -0
  240. package/dist/utils/ip.mjs +119 -0
  241. package/dist/utils/ip.mjs.map +1 -0
  242. package/dist/utils/json.d.mts +2 -1
  243. package/dist/utils/json.mjs +2 -1
  244. package/dist/utils/json.mjs.map +1 -0
  245. package/dist/utils/string.d.mts +2 -1
  246. package/dist/utils/string.mjs +2 -1
  247. package/dist/utils/string.mjs.map +1 -0
  248. package/dist/utils/url.d.mts +2 -1
  249. package/dist/utils/url.mjs +2 -1
  250. package/dist/utils/url.mjs.map +1 -0
  251. package/package.json +35 -13
  252. package/src/context/index.ts +1 -0
  253. package/src/context/transaction.ts +72 -9
  254. package/src/db/adapter/factory.ts +41 -73
  255. package/src/db/adapter/get-id-field.ts +1 -3
  256. package/src/db/adapter/index.ts +20 -15
  257. package/src/db/adapter/types.ts +2 -41
  258. package/src/db/get-tables.ts +48 -37
  259. package/src/db/index.ts +30 -5
  260. package/src/db/schema/account.ts +16 -3
  261. package/src/db/schema/rate-limit.ts +16 -1
  262. package/src/db/schema/session.ts +15 -3
  263. package/src/db/schema/user.ts +15 -3
  264. package/src/db/schema/verification.ts +16 -3
  265. package/src/db/test/get-tables.test.ts +33 -0
  266. package/src/db/type.ts +154 -1
  267. package/src/env/env-impl.ts +2 -2
  268. package/src/env/logger.ts +1 -1
  269. package/src/error/codes.ts +17 -0
  270. package/src/oauth2/client-credentials-token.ts +26 -2
  271. package/src/oauth2/create-authorization-url.ts +3 -1
  272. package/src/oauth2/index.ts +3 -0
  273. package/src/oauth2/oauth-provider.ts +1 -1
  274. package/src/oauth2/refresh-access-token.test.ts +90 -0
  275. package/src/oauth2/refresh-access-token.ts +37 -4
  276. package/src/oauth2/validate-authorization-code.ts +55 -29
  277. package/src/oauth2/validate-token.test.ts +229 -0
  278. package/src/social-providers/apple.ts +29 -29
  279. package/src/social-providers/cognito.ts +6 -5
  280. package/src/social-providers/dropbox.ts +1 -1
  281. package/src/social-providers/facebook.ts +3 -3
  282. package/src/social-providers/github.ts +26 -4
  283. package/src/social-providers/gitlab.ts +1 -1
  284. package/src/social-providers/google.ts +18 -14
  285. package/src/social-providers/huggingface.ts +1 -1
  286. package/src/social-providers/index.ts +9 -5
  287. package/src/social-providers/kakao.ts +1 -1
  288. package/src/social-providers/line.ts +1 -1
  289. package/src/social-providers/microsoft-entra-id.ts +84 -1
  290. package/src/social-providers/naver.ts +1 -1
  291. package/src/social-providers/notion.ts +1 -1
  292. package/src/social-providers/paybin.ts +1 -5
  293. package/src/social-providers/polar.ts +1 -1
  294. package/src/social-providers/railway.ts +100 -0
  295. package/src/social-providers/tiktok.ts +2 -1
  296. package/src/social-providers/vercel.ts +1 -1
  297. package/src/social-providers/zoom.ts +0 -8
  298. package/src/types/context.ts +79 -15
  299. package/src/types/helper.ts +9 -0
  300. package/src/types/index.ts +14 -2
  301. package/src/types/init-options.ts +298 -171
  302. package/src/types/plugin-client.ts +1 -0
  303. package/src/types/plugin.ts +11 -6
  304. package/src/types/secret.ts +8 -0
  305. package/src/utils/db.ts +20 -0
  306. package/src/utils/deprecate.test.ts +0 -1
  307. package/src/utils/error-codes.ts +12 -9
  308. package/src/utils/ip.test.ts +255 -0
  309. package/src/utils/ip.ts +211 -0
  310. package/.turbo/turbo-build.log +0 -180
  311. package/tsconfig.json +0 -7
  312. package/tsdown.config.ts +0 -32
  313. package/vitest.config.ts +0 -3
@@ -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
@@ -136,6 +206,13 @@ export type BetterAuthAdvancedOptions = {
136
206
  * ⚠︎ This is a security risk and it may expose your application to abuse
137
207
  */
138
208
  disableIpTracking?: boolean;
209
+ /**
210
+ * IPv6 subnet prefix length for rate limiting.
211
+ * IPv6 addresses will be normalized to this subnet.
212
+ *
213
+ * @default 64
214
+ */
215
+ ipv6Subnet?: 128 | 64 | 48 | 32;
139
216
  }
140
217
  | undefined;
141
218
  /**
@@ -237,17 +314,6 @@ export type BetterAuthAdvancedOptions = {
237
314
  * @default 100
238
315
  */
239
316
  defaultFindManyLimit?: number;
240
- /**
241
- * If your database auto increments number ids, set this to `true`.
242
- *
243
- * 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.
244
- *
245
- * @default false
246
- *
247
- * @deprecated Please use `generateId` instead. This will be removed in future
248
- * releases.
249
- */
250
- useNumberId?: boolean;
251
317
  /**
252
318
  * Custom generateId function.
253
319
  *
@@ -303,14 +369,17 @@ export type BetterAuthAdvancedOptions = {
303
369
  * }
304
370
  */
305
371
  backgroundTasks?: {
306
- handler: (promise: Promise<void>) => void;
372
+ handler: (promise: Promise<unknown>) => void;
307
373
  };
308
374
  /**
309
- * 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`).
310
379
  *
311
380
  * @default false
312
381
  */
313
- skipTrailingSlashes?: boolean | undefined;
382
+ skipTrailingSlashes?: boolean;
314
383
  };
315
384
 
316
385
  export type BetterAuthOptions = {
@@ -325,12 +394,27 @@ export type BetterAuthOptions = {
325
394
  /**
326
395
  * Base URL for the Better Auth. This is typically the
327
396
  * root URL where your application server is hosted.
328
- * If not explicitly set,
329
- * the system will check the following environment variable:
330
397
  *
331
- * 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
+ * ```
332
416
  */
333
- baseURL?: string | undefined;
417
+ baseURL?: BaseURLConfig | undefined;
334
418
  /**
335
419
  * Base path for the Better Auth. This is typically
336
420
  * the path where the
@@ -363,6 +447,19 @@ export type BetterAuthOptions = {
363
447
  * ```
364
448
  */
365
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;
366
463
  /**
367
464
  * Database configuration
368
465
  */
@@ -375,6 +472,7 @@ export type BetterAuthOptions = {
375
472
  | DBAdapterInstance
376
473
  | BunDatabase
377
474
  | DatabaseSync
475
+ | D1Database
378
476
  | {
379
477
  dialect: Dialect;
380
478
  type: KyselyDatabaseType;
@@ -466,10 +564,13 @@ export type BetterAuthOptions = {
466
564
  request?: Request,
467
565
  ) => Promise<void>;
468
566
  /**
469
- * Send a verification email automatically
470
- * after sign up
567
+ * Send a verification email automatically after sign up.
471
568
  *
472
- * @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
473
574
  */
474
575
  sendOnSignUp?: boolean;
475
576
  /**
@@ -489,13 +590,6 @@ export type BetterAuthOptions = {
489
590
  * @default 3600 seconds (1 hour)
490
591
  */
491
592
  expiresIn?: number;
492
- /**
493
- * A function that is called when a user verifies their email
494
- * @param user the user that verified their email
495
- * @param request the request object
496
- * @deprecated Use `beforeEmailVerification` or `afterEmailVerification` instead. This will be removed in 1.5
497
- */
498
- onEmailVerification?: (user: User, request?: Request) => Promise<void>;
499
593
  /**
500
594
  * A function that is called before a user verifies their email
501
595
  * @param user the user that verified their email
@@ -610,6 +704,59 @@ export type BetterAuthOptions = {
610
704
  * @default false
611
705
  */
612
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>;
613
760
  }
614
761
  | undefined;
615
762
  /**
@@ -624,28 +771,7 @@ export type BetterAuthOptions = {
624
771
  * User configuration
625
772
  */
626
773
  user?:
627
- | {
628
- /**
629
- * The model name for the user. Defaults to "user".
630
- */
631
- modelName?: string;
632
- /**
633
- * Map fields
634
- *
635
- * @example
636
- * ```ts
637
- * {
638
- * userId: "user_id"
639
- * }
640
- * ```
641
- */
642
- fields?: Partial<Record<keyof OmitId<User>, string>>;
643
- /**
644
- * Additional fields for the user
645
- */
646
- additionalFields?: {
647
- [key: string]: DBFieldAttribute;
648
- };
774
+ | (BetterAuthDBOptions<"user", keyof BaseUser> & {
649
775
  /**
650
776
  * Changing email configuration
651
777
  */
@@ -655,21 +781,6 @@ export type BetterAuthOptions = {
655
781
  * @default false
656
782
  */
657
783
  enabled: boolean;
658
- /**
659
- * Send a verification email when the user changes their email.
660
- * @param data the data object
661
- * @param request the request object
662
- * @deprecated Use `sendChangeEmailConfirmation` instead
663
- */
664
- sendChangeEmailVerification?: (
665
- data: {
666
- user: User;
667
- newEmail: string;
668
- url: string;
669
- token: string;
670
- },
671
- request?: Request,
672
- ) => Promise<void>;
673
784
  /**
674
785
  * Send a confirmation email to the old email address when the user changes their email.
675
786
  * @param data the data object
@@ -732,26 +843,10 @@ export type BetterAuthOptions = {
732
843
  */
733
844
  deleteTokenExpiresIn?: number;
734
845
  };
735
- }
846
+ })
736
847
  | undefined;
737
848
  session?:
738
- | {
739
- /**
740
- * The model name for the session.
741
- *
742
- * @default "session"
743
- */
744
- modelName?: string;
745
- /**
746
- * Map fields
747
- *
748
- * @example
749
- * ```ts
750
- * {
751
- * userId: "user_id"
752
- * }
753
- */
754
- fields?: Partial<Record<keyof OmitId<Session>, string>>;
849
+ | (BetterAuthDBOptions<"session", keyof BaseSession> & {
755
850
  /**
756
851
  * Expiration time for the session token. The value
757
852
  * should be in seconds.
@@ -773,11 +868,13 @@ export type BetterAuthOptions = {
773
868
  */
774
869
  disableSessionRefresh?: boolean;
775
870
  /**
776
- * 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
777
876
  */
778
- additionalFields?: {
779
- [key: string]: DBFieldAttribute;
780
- };
877
+ deferSessionRefresh?: boolean;
781
878
  /**
782
879
  * By default if secondary storage is provided
783
880
  * the session is stored in the secondary storage.
@@ -887,24 +984,10 @@ export type BetterAuthOptions = {
887
984
  * @default 1 day (60 * 60 * 24)
888
985
  */
889
986
  freshAge?: number;
890
- }
987
+ })
891
988
  | undefined;
892
989
  account?:
893
- | {
894
- /**
895
- * The model name for the account. Defaults to "account".
896
- */
897
- modelName?: string;
898
- /**
899
- * Map fields
900
- */
901
- fields?: Partial<Record<keyof OmitId<Account>, string>>;
902
- /**
903
- * Additional fields for the account
904
- */
905
- additionalFields?: {
906
- [key: string]: DBFieldAttribute;
907
- };
990
+ | (BetterAuthDBOptions<"account", keyof BaseAccount> & {
908
991
  /**
909
992
  * When enabled (true), the user account data (accessToken, idToken, refreshToken, etc.)
910
993
  * will be updated on sign in with the latest data from the provider.
@@ -923,11 +1006,54 @@ export type BetterAuthOptions = {
923
1006
  */
924
1007
  enabled?: boolean;
925
1008
  /**
926
- * 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
+ * ```
927
1039
  */
928
- trustedProviders?: Array<
929
- LiteralUnion<SocialProviderList[number] | "email-password", string>
930
- >;
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
+ >);
931
1057
  /**
932
1058
  * If enabled (true), this will allow users to manually linking accounts with different email addresses than the main user.
933
1059
  *
@@ -989,33 +1115,34 @@ export type BetterAuthOptions = {
989
1115
  * @note This is automatically set to true if you haven't passed a database
990
1116
  */
991
1117
  storeAccountCookie?: boolean;
992
- }
1118
+ })
993
1119
  | undefined;
994
- /**
995
- * Verification configuration
996
- */
997
1120
  verification?:
998
- | {
1121
+ | (BetterAuthDBOptions<"verification", keyof BaseVerification> & {
999
1122
  /**
1000
- * Change the modelName of the verification table
1001
- */
1002
- modelName?: string;
1003
- /**
1004
- * Map verification fields
1123
+ * disable cleaning up expired values when a verification value is
1124
+ * fetched
1005
1125
  */
1006
- fields?: Partial<Record<keyof OmitId<Verification>, string>>;
1126
+ disableCleanup?: boolean;
1007
1127
  /**
1008
- * Additional fields for the verification
1128
+ * How to store verification identifiers (tokens, OTPs, etc.)
1129
+ *
1130
+ * @example "hashed"
1131
+ *
1132
+ * @default "plain"
1009
1133
  */
1010
- additionalFields?: {
1011
- [key: string]: DBFieldAttribute;
1012
- };
1134
+ storeIdentifier?:
1135
+ | StoreIdentifierOption
1136
+ | {
1137
+ default: StoreIdentifierOption;
1138
+ overrides?: Record<string, StoreIdentifierOption>;
1139
+ };
1013
1140
  /**
1014
- * disable cleaning up expired values when a verification value is
1015
- * fetched
1141
+ * Store verification data in database even when secondary storage is configured.
1142
+ * @default false
1016
1143
  */
1017
- disableCleanup?: boolean;
1018
- }
1144
+ storeInDatabase?: boolean;
1145
+ })
1019
1146
  | undefined;
1020
1147
  /**
1021
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
  /**
@@ -7,6 +7,7 @@ import type {
7
7
  import type { Migration } from "kysely";
8
8
  import type { AuthMiddleware } from "../api";
9
9
  import type { BetterAuthPluginDBSchema } from "../db";
10
+ import type { RawError } from "../utils/error-codes";
10
11
  import type { AuthContext } from "./context";
11
12
  import type { Awaitable, LiteralString } from "./helper";
12
13
  import type { BetterAuthOptions } from "./init-options";
@@ -28,7 +29,14 @@ export type HookEndpointContext = Partial<
28
29
  headers?: Headers | undefined;
29
30
  };
30
31
 
31
- export type BetterAuthPlugin = {
32
+ export type BetterAuthPluginErrorCodePart = {
33
+ /**
34
+ * The error codes returned by the plugin
35
+ */
36
+ $ERROR_CODES?: Record<string, RawError>;
37
+ };
38
+
39
+ export type BetterAuthPlugin = BetterAuthPluginErrorCodePart & {
32
40
  id: LiteralString;
33
41
  /**
34
42
  * The init function is called when the plugin is initialized.
@@ -37,7 +45,8 @@ export type BetterAuthPlugin = {
37
45
  init?:
38
46
  | ((ctx: AuthContext) =>
39
47
  | Awaitable<{
40
- context?: DeepPartial<Omit<AuthContext, "options">>;
48
+ context?: DeepPartial<Omit<AuthContext, "options">> &
49
+ Record<string, unknown>;
41
50
  options?: Partial<BetterAuthOptions>;
42
51
  }>
43
52
  | void
@@ -142,10 +151,6 @@ export type BetterAuthPlugin = {
142
151
  pathMatcher: (path: string) => boolean;
143
152
  }[]
144
153
  | undefined;
145
- /**
146
- * The error codes returned by the plugin
147
- */
148
- $ERROR_CODES?: Record<string, { code: string; message: string }> | undefined;
149
154
  /**
150
155
  * All database operations that are performed by the plugin
151
156
  *
@@ -0,0 +1,8 @@
1
+ export interface SecretConfig {
2
+ /** Map of version number → secret value */
3
+ keys: Map<number, string>;
4
+ /** Version to use for new encryption (first entry in secrets array) */
5
+ currentVersion: number;
6
+ /** Legacy secret for bare-hex fallback (from BETTER_AUTH_SECRET) */
7
+ legacySecret?: string;
8
+ }