better-auth 1.7.0-beta.5 → 1.7.0-beta.6

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 (263) hide show
  1. package/dist/_virtual/_rolldown/runtime.mjs +1 -10
  2. package/dist/api/index.d.mts +3 -3
  3. package/dist/api/index.mjs +4 -5
  4. package/dist/api/middlewares/origin-check.mjs +5 -1
  5. package/dist/api/rate-limiter/index.mjs +213 -95
  6. package/dist/api/routes/account.mjs +22 -7
  7. package/dist/api/routes/callback.mjs +2 -2
  8. package/dist/api/routes/email-verification.mjs +19 -6
  9. package/dist/api/routes/index.d.mts +1 -1
  10. package/dist/api/routes/password.mjs +3 -4
  11. package/dist/api/routes/session.d.mts +12 -1
  12. package/dist/api/routes/session.mjs +94 -83
  13. package/dist/api/routes/sign-in.mjs +5 -5
  14. package/dist/api/routes/sign-up.mjs +2 -2
  15. package/dist/api/routes/update-session.mjs +2 -3
  16. package/dist/api/routes/update-user.mjs +10 -12
  17. package/dist/auth/base.mjs +11 -7
  18. package/dist/client/config.d.mts +88 -0
  19. package/dist/client/equality.d.mts +19 -0
  20. package/dist/client/equality.mjs +42 -0
  21. package/dist/client/index.d.mts +5 -4
  22. package/dist/client/index.mjs +2 -1
  23. package/dist/client/lynx/index.d.mts +21 -118
  24. package/dist/client/path-to-object.d.mts +5 -2
  25. package/dist/client/plugins/index.d.mts +4 -3
  26. package/dist/client/plugins/index.mjs +4 -2
  27. package/dist/client/query.d.mts +4 -3
  28. package/dist/client/query.mjs +27 -17
  29. package/dist/client/react/index.d.mts +21 -118
  30. package/dist/client/session-atom.d.mts +11 -0
  31. package/dist/client/session-atom.mjs +129 -4
  32. package/dist/client/session-refresh.d.mts +3 -18
  33. package/dist/client/session-refresh.mjs +38 -49
  34. package/dist/client/solid/index.d.mts +20 -112
  35. package/dist/client/svelte/index.d.mts +21 -118
  36. package/dist/client/types.d.mts +27 -16
  37. package/dist/client/vanilla.d.mts +20 -118
  38. package/dist/client/vue/index.d.mts +37 -145
  39. package/dist/context/create-context.mjs +14 -2
  40. package/dist/context/helpers.mjs +5 -4
  41. package/dist/context/store-capabilities.mjs +12 -0
  42. package/dist/cookies/index.d.mts +7 -0
  43. package/dist/cookies/index.mjs +49 -19
  44. package/dist/cookies/session-store.d.mts +0 -17
  45. package/dist/cookies/session-store.mjs +42 -51
  46. package/dist/db/internal-adapter.mjs +43 -13
  47. package/dist/{packages/better-auth/package.mjs → package.mjs} +1 -1
  48. package/dist/plugins/access/access.mjs +49 -19
  49. package/dist/plugins/admin/routes.mjs +10 -3
  50. package/dist/plugins/captcha/constants.mjs +8 -1
  51. package/dist/plugins/captcha/index.mjs +17 -13
  52. package/dist/plugins/captcha/types.d.mts +25 -0
  53. package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -0
  54. package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +7 -2
  55. package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +7 -2
  56. package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -0
  57. package/dist/plugins/device-authorization/index.d.mts +3 -0
  58. package/dist/plugins/device-authorization/routes.mjs +123 -100
  59. package/dist/plugins/email-otp/index.mjs +1 -1
  60. package/dist/plugins/email-otp/otp-token.mjs +1 -1
  61. package/dist/plugins/email-otp/routes.mjs +23 -53
  62. package/dist/plugins/generic-oauth/index.d.mts +2 -2
  63. package/dist/plugins/generic-oauth/index.mjs +15 -11
  64. package/dist/plugins/generic-oauth/types.d.mts +8 -3
  65. package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
  66. package/dist/plugins/haveibeenpwned/index.mjs +5 -1
  67. package/dist/plugins/index.d.mts +7 -6
  68. package/dist/plugins/index.mjs +6 -5
  69. package/dist/plugins/jwt/adapter.mjs +21 -2
  70. package/dist/plugins/jwt/cookie-cache.mjs +117 -0
  71. package/dist/plugins/jwt/index.d.mts +8 -0
  72. package/dist/plugins/jwt/index.mjs +6 -6
  73. package/dist/plugins/jwt/schema.d.mts +8 -0
  74. package/dist/plugins/jwt/schema.mjs +8 -0
  75. package/dist/plugins/jwt/sign.d.mts +51 -2
  76. package/dist/plugins/jwt/sign.mjs +52 -6
  77. package/dist/plugins/jwt/types.d.mts +36 -0
  78. package/dist/plugins/jwt/utils.mjs +6 -3
  79. package/dist/plugins/last-login-method/client.d.mts +10 -0
  80. package/dist/plugins/last-login-method/client.mjs +4 -1
  81. package/dist/plugins/multi-session/index.mjs +7 -5
  82. package/dist/plugins/oauth-popup/client.d.mts +82 -0
  83. package/dist/plugins/oauth-popup/client.mjs +203 -0
  84. package/dist/plugins/oauth-popup/constants.d.mts +11 -0
  85. package/dist/plugins/oauth-popup/constants.mjs +11 -0
  86. package/dist/plugins/oauth-popup/error-codes.d.mts +11 -0
  87. package/dist/plugins/oauth-popup/error-codes.mjs +10 -0
  88. package/dist/plugins/oauth-popup/index.d.mts +67 -0
  89. package/dist/plugins/oauth-popup/index.mjs +228 -0
  90. package/dist/plugins/oauth-popup/types.d.mts +30 -0
  91. package/dist/plugins/oauth-proxy/index.mjs +3 -3
  92. package/dist/plugins/oauth-proxy/utils.mjs +16 -2
  93. package/dist/plugins/one-tap/client.mjs +12 -6
  94. package/dist/plugins/one-tap/index.d.mts +1 -2
  95. package/dist/plugins/one-tap/index.mjs +13 -8
  96. package/dist/plugins/one-time-token/index.mjs +1 -3
  97. package/dist/plugins/open-api/generator.d.mts +67 -58
  98. package/dist/plugins/open-api/generator.mjs +241 -107
  99. package/dist/plugins/open-api/index.d.mts +2 -2
  100. package/dist/plugins/organization/adapter.d.mts +33 -7
  101. package/dist/plugins/organization/adapter.mjs +219 -44
  102. package/dist/plugins/organization/client.d.mts +4 -4
  103. package/dist/plugins/organization/organization.mjs +16 -0
  104. package/dist/plugins/organization/routes/crud-access-control.mjs +1 -1
  105. package/dist/plugins/organization/routes/crud-invites.mjs +49 -33
  106. package/dist/plugins/organization/routes/crud-members.mjs +59 -6
  107. package/dist/plugins/organization/routes/crud-team.d.mts +5 -8
  108. package/dist/plugins/organization/routes/crud-team.mjs +40 -3
  109. package/dist/plugins/organization/schema.d.mts +14 -0
  110. package/dist/plugins/organization/types.d.mts +2 -2
  111. package/dist/plugins/phone-number/index.d.mts +12 -0
  112. package/dist/plugins/phone-number/index.mjs +2 -1
  113. package/dist/plugins/phone-number/routes.mjs +80 -44
  114. package/dist/plugins/siwe/index.mjs +2 -3
  115. package/dist/plugins/two-factor/backup-codes/index.mjs +5 -4
  116. package/dist/plugins/two-factor/otp/index.mjs +11 -13
  117. package/dist/plugins/two-factor/totp/index.mjs +1 -1
  118. package/dist/plugins/two-factor/verify-two-factor.mjs +6 -2
  119. package/dist/plugins/username/index.mjs +6 -6
  120. package/dist/state.mjs +2 -1
  121. package/dist/test-utils/http-test-instance.d.mts +9 -3
  122. package/dist/test-utils/http-test-instance.mjs +49 -8
  123. package/dist/test-utils/test-instance.d.mts +3 -16135
  124. package/dist/version.mjs +1 -1
  125. package/package.json +10 -35
  126. package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/index.mjs +0 -32
  127. package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/linux.mjs +0 -55
  128. package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/macos.mjs +0 -26
  129. package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/termux.mjs +0 -39
  130. package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/windows.mjs +0 -19
  131. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/chunks/prompt.mjs +0 -845
  132. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/core.mjs +0 -386
  133. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/index.mjs +0 -320
  134. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/shared/consola.DRwqZj3T.mjs +0 -62
  135. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/shared/consola.DXBYu-KD.mjs +0 -190
  136. package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/utils.mjs +0 -2
  137. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.mjs +0 -29
  138. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.mjs +0 -42
  139. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.mjs +0 -67
  140. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.mjs +0 -23
  141. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.mjs +0 -22
  142. package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.mjs +0 -36
  143. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/adapters/node.d.mts +0 -292
  144. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/adapters/node.mjs +0 -127
  145. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/index.d.mts +0 -145
  146. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.BQXMA5bH.d.mts +0 -298
  147. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.By9qWDAI.mjs +0 -9
  148. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.CipVM6lf.mjs +0 -3549
  149. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.D9ehKjSh.mjs +0 -66
  150. package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.DfCzGthR.mjs +0 -227
  151. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/index.mjs +0 -224
  152. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/command.mjs +0 -52
  153. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/error.mjs +0 -54
  154. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/kill.mjs +0 -62
  155. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/pipe.mjs +0 -26
  156. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/promise.mjs +0 -32
  157. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/stdio.mjs +0 -19
  158. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/stream.mjs +0 -98
  159. package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/verbose.mjs +0 -15
  160. package/dist/node_modules/.pnpm/get-port-please@3.2.0/node_modules/get-port-please/dist/index.d.mts +0 -15
  161. package/dist/node_modules/.pnpm/get-port-please@3.2.0/node_modules/get-port-please/dist/index.mjs +0 -220
  162. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/array-buffer.mjs +0 -54
  163. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/array.mjs +0 -2
  164. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/buffer.mjs +0 -14
  165. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/contents.mjs +0 -76
  166. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/index.mjs +0 -6
  167. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/string.mjs +0 -35
  168. package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/utils.mjs +0 -10
  169. package/dist/node_modules/.pnpm/http-shutdown@1.2.2/node_modules/http-shutdown/index.mjs +0 -76
  170. package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/core.mjs +0 -274
  171. package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/main.mjs +0 -44
  172. package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/realtime.mjs +0 -15
  173. package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/signals.mjs +0 -23
  174. package/dist/node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.mjs +0 -24
  175. package/dist/node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.mjs +0 -18
  176. package/dist/node_modules/.pnpm/is-stream@3.0.0/node_modules/is-stream/index.mjs +0 -9
  177. package/dist/node_modules/.pnpm/is-wsl@3.1.1/node_modules/is-wsl/index.mjs +0 -20
  178. package/dist/node_modules/.pnpm/is64bit@2.0.0/node_modules/is64bit/index.mjs +0 -13
  179. package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.mjs +0 -47
  180. package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.mjs +0 -33
  181. package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.mjs +0 -33
  182. package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/chunks/xdg-open.mjs +0 -1070
  183. package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/index.mjs +0 -619
  184. package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/shared/listhen.1c46e31d.d.mts +0 -83
  185. package/dist/node_modules/.pnpm/merge-stream@2.0.0/node_modules/merge-stream/index.mjs +0 -38
  186. package/dist/node_modules/.pnpm/mimic-fn@4.0.0/node_modules/mimic-fn/index.mjs +0 -38
  187. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/aes.mjs +0 -597
  188. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/aesCipherSuites.mjs +0 -195
  189. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/asn1-validator.mjs +0 -76
  190. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/asn1.mjs +0 -967
  191. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/baseN.mjs +0 -137
  192. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/cipher.mjs +0 -186
  193. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/cipherModes.mjs +0 -597
  194. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/des.mjs +0 -1187
  195. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/ed25519.mjs +0 -1029
  196. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/forge.mjs +0 -15
  197. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/hmac.mjs +0 -107
  198. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/index.mjs +0 -66
  199. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/jsbn.mjs +0 -1334
  200. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/kem.mjs +0 -146
  201. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/log.mjs +0 -241
  202. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md.all.mjs +0 -24
  203. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md.mjs +0 -18
  204. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md5.mjs +0 -324
  205. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/mgf.mjs +0 -20
  206. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/mgf1.mjs +0 -44
  207. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/oids.mjs +0 -154
  208. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pbe.mjs +0 -815
  209. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pbkdf2.mjs +0 -125
  210. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pem.mjs +0 -175
  211. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs1.mjs +0 -200
  212. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs12.mjs +0 -724
  213. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs7.mjs +0 -642
  214. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs7asn1.mjs +0 -405
  215. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pki.mjs +0 -101
  216. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/prime.mjs +0 -193
  217. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/prng.mjs +0 -290
  218. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pss.mjs +0 -141
  219. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/random.mjs +0 -141
  220. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/rc2.mjs +0 -538
  221. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/rsa.mjs +0 -1309
  222. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha1.mjs +0 -230
  223. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha256.mjs +0 -267
  224. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha512.mjs +0 -413
  225. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/ssh.mjs +0 -194
  226. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/tls.mjs +0 -3655
  227. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/util.mjs +0 -2117
  228. package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/x509.mjs +0 -2168
  229. package/dist/node_modules/.pnpm/npm-run-path@5.3.0/node_modules/npm-run-path/index.mjs +0 -34
  230. package/dist/node_modules/.pnpm/onetime@6.0.0/node_modules/onetime/index.mjs +0 -26
  231. package/dist/node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.mjs +0 -14
  232. package/dist/node_modules/.pnpm/path-key@4.0.0/node_modules/path-key/index.mjs +0 -8
  233. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.mjs +0 -176
  234. package/dist/node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.mjs +0 -17
  235. package/dist/node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.mjs +0 -8
  236. package/dist/node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.mjs +0 -169
  237. package/dist/node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/signals.mjs +0 -33
  238. package/dist/node_modules/.pnpm/std-env@3.10.0/node_modules/std-env/dist/index.mjs +0 -171
  239. package/dist/node_modules/.pnpm/strip-final-newline@3.0.0/node_modules/strip-final-newline/index.mjs +0 -10
  240. package/dist/node_modules/.pnpm/system-architecture@0.1.0/node_modules/system-architecture/index.mjs +0 -16
  241. package/dist/node_modules/.pnpm/uncrypto@0.1.3/node_modules/uncrypto/dist/crypto.node.mjs +0 -7
  242. package/dist/node_modules/.pnpm/untun@0.1.3/node_modules/untun/dist/chunks/index.mjs +0 -154
  243. package/dist/node_modules/.pnpm/untun@0.1.3/node_modules/untun/dist/index.mjs +0 -34
  244. package/dist/node_modules/.pnpm/uqr@0.1.2/node_modules/uqr/dist/index.mjs +0 -896
  245. package/dist/node_modules/.pnpm/which@2.0.2/node_modules/which/which.mjs +0 -76
  246. package/dist/plugins/mcp/authorize.mjs +0 -134
  247. package/dist/plugins/mcp/client/adapters.d.mts +0 -56
  248. package/dist/plugins/mcp/client/adapters.mjs +0 -117
  249. package/dist/plugins/mcp/client/index.d.mts +0 -44
  250. package/dist/plugins/mcp/client/index.mjs +0 -152
  251. package/dist/plugins/mcp/index.d.mts +0 -457
  252. package/dist/plugins/mcp/index.mjs +0 -770
  253. package/dist/plugins/oidc-provider/authorize.mjs +0 -204
  254. package/dist/plugins/oidc-provider/client.d.mts +0 -16
  255. package/dist/plugins/oidc-provider/client.mjs +0 -15
  256. package/dist/plugins/oidc-provider/error.mjs +0 -24
  257. package/dist/plugins/oidc-provider/index.d.mts +0 -705
  258. package/dist/plugins/oidc-provider/index.mjs +0 -1122
  259. package/dist/plugins/oidc-provider/schema.d.mts +0 -159
  260. package/dist/plugins/oidc-provider/schema.mjs +0 -129
  261. package/dist/plugins/oidc-provider/types.d.mts +0 -517
  262. package/dist/plugins/oidc-provider/utils/prompt.mjs +0 -16
  263. package/dist/plugins/oidc-provider/utils.mjs +0 -12
@@ -1,12 +1,8 @@
1
- import { createRequire } from "node:module";
2
1
  //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
2
  var __defProp = Object.defineProperty;
5
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
10
6
  var __exportAll = (all, no_symbols) => {
11
7
  let target = {};
12
8
  for (var name in all) __defProp(target, name, {
@@ -27,10 +23,5 @@ var __copyProps = (to, from, except, desc) => {
27
23
  return to;
28
24
  };
29
25
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
31
- value: mod,
32
- enumerable: true
33
- }) : target, mod));
34
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
35
26
  //#endregion
36
- export { __commonJSMin, __exportAll, __reExport, __require, __toESM };
27
+ export { __exportAll, __reExport };
@@ -11,7 +11,7 @@ import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEm
11
11
  import { error } from "./routes/error.mjs";
12
12
  import { ok } from "./routes/ok.mjs";
13
13
  import { requestPasswordReset, requestPasswordResetCallback, resetPassword, verifyPassword } from "./routes/password.mjs";
14
- import { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
14
+ import { freshSessionMiddleware, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
15
15
  import { signInEmail, signInSocial } from "./routes/sign-in.mjs";
16
16
  import { signOut } from "./routes/sign-out.mjs";
17
17
  import { signUpEmail } from "./routes/sign-up.mjs";
@@ -25,7 +25,7 @@ import { InternalLogger } from "@better-auth/core/env";
25
25
  import { APIError } from "@better-auth/core/error";
26
26
  import * as _better_auth_core_oauth20 from "@better-auth/core/oauth2";
27
27
  import * as better_call0 from "better-call";
28
- import { AuthEndpoint, AuthMiddleware, createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
28
+ import { AuthEndpoint, AuthMiddleware, NO_STORE_HEADERS, createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
29
29
  import * as zod from "zod";
30
30
  import * as zod_v4_core0 from "zod/v4/core";
31
31
 
@@ -3989,4 +3989,4 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
3989
3989
  } extends infer T_2 ? { [K in keyof T_2 as K extends keyof T_1 ? never : K]: T_2[K] } : never) & T_1> : never : never : never;
3990
3990
  };
3991
3991
  //#endregion
3992
- export { APIError, type AuthEndpoint, type AuthMiddleware, type DispatchContext, accountInfo, addOAuthServerContext, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, dispatchAuthEndpoint, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, getShouldSkipSessionRefresh, isAPIError, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, requireOrgRole, requireResourceOwnership, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, setShouldSkipSessionRefresh, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateSession, updateUser, verifyEmail, verifyPassword };
3992
+ export { APIError, type AuthEndpoint, type AuthMiddleware, type DispatchContext, NO_STORE_HEADERS, accountInfo, addOAuthServerContext, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, dispatchAuthEndpoint, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, getShouldSkipSessionRefresh, isAPIError, isStateful, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, requireOrgRole, requireResourceOwnership, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, setShouldSkipSessionRefresh, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateSession, updateUser, verifyEmail, verifyPassword };
@@ -2,10 +2,10 @@ import { isAPIError } from "../utils/is-api-error.mjs";
2
2
  import { requireOrgRole, requireResourceOwnership } from "./middlewares/authorization.mjs";
3
3
  import { formCsrfMiddleware, originCheck, originCheckMiddleware } from "./middlewares/origin-check.mjs";
4
4
  import { getIp } from "../utils/get-request-ip.mjs";
5
- import { onRequestRateLimit, onResponseRateLimit } from "./rate-limiter/index.mjs";
5
+ import { onRequestRateLimit } from "./rate-limiter/index.mjs";
6
6
  import { addOAuthServerContext, getOAuthState } from "./state/oauth.mjs";
7
7
  import { getShouldSkipSessionRefresh, setShouldSkipSessionRefresh } from "./state/should-session-refresh.mjs";
8
- import { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
8
+ import { freshSessionMiddleware, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
9
9
  import { accountInfo, getAccessToken, linkSocialAccount, listUserAccounts, refreshToken, unlinkAccount } from "./routes/account.mjs";
10
10
  import { callbackOAuth } from "./routes/callback.mjs";
11
11
  import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEmailFn, verifyEmail } from "./routes/email-verification.mjs";
@@ -24,7 +24,7 @@ import { APIError } from "@better-auth/core/error";
24
24
  import { ATTR_CONTEXT, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, withSpan } from "@better-auth/core/instrumentation";
25
25
  import { normalizePathname } from "@better-auth/core/utils/url";
26
26
  import { createRouter } from "better-call";
27
- import { createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
27
+ import { NO_STORE_HEADERS, createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
28
28
  //#region src/api/index.ts
29
29
  function checkEndpointConflicts(options, logger) {
30
30
  const endpointRegistry = /* @__PURE__ */ new Map();
@@ -178,7 +178,6 @@ const router = (ctx, options) => {
178
178
  return currentRequest;
179
179
  },
180
180
  async onResponse(res, req) {
181
- await onResponseRateLimit(req, ctx);
182
181
  for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) {
183
182
  const response = await withSpan(`onResponse ${plugin.id}`, {
184
183
  [ATTR_HOOK_TYPE]: "onResponse",
@@ -214,4 +213,4 @@ const router = (ctx, options) => {
214
213
  });
215
214
  };
216
215
  //#endregion
217
- export { APIError, accountInfo, addOAuthServerContext, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, dispatchAuthEndpoint, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, getShouldSkipSessionRefresh, isAPIError, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, requireOrgRole, requireResourceOwnership, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, setShouldSkipSessionRefresh, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateSession, updateUser, verifyEmail, verifyPassword };
216
+ export { APIError, NO_STORE_HEADERS, accountInfo, addOAuthServerContext, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, dispatchAuthEndpoint, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, getShouldSkipSessionRefresh, isAPIError, isStateful, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, requireOrgRole, requireResourceOwnership, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, setShouldSkipSessionRefresh, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateSession, updateUser, verifyEmail, verifyPassword };
@@ -23,7 +23,10 @@ function shouldSkipOriginCheck(ctx) {
23
23
  if (Array.isArray(skipOriginCheck) && ctx.request) try {
24
24
  const basePath = new URL(ctx.context.baseURL).pathname;
25
25
  const currentPath = normalizePathname(ctx.request.url, basePath);
26
- return skipOriginCheck.some((skipPath) => currentPath.startsWith(skipPath));
26
+ return skipOriginCheck.some((skipPath) => {
27
+ const normalizedSkipPath = skipPath.replace(/\/+$/, "");
28
+ return currentPath === normalizedSkipPath || currentPath.startsWith(`${normalizedSkipPath}/`);
29
+ });
27
30
  } catch {}
28
31
  return false;
29
32
  }
@@ -47,6 +50,7 @@ const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
47
50
  const newUserCallbackURL = body?.newUserCallbackURL;
48
51
  const validateURL = (url, label) => {
49
52
  if (!url) return;
53
+ if (typeof url !== "string") throw APIError.fromStatus("BAD_REQUEST", { message: `Invalid ${label}: expected a string` });
50
54
  if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) {
51
55
  ctx.context.logger.error(`Invalid ${label}: ${url}`);
52
56
  ctx.context.logger.info(`If it's a valid URL, please add ${url} to trustedOrigins in your auth config\n`, `Current list of trustedOrigins: ${ctx.context.trustedOrigins}`);
@@ -1,14 +1,66 @@
1
1
  import { wildcardMatch } from "../../utils/wildcard.mjs";
2
2
  import { getIp } from "../../utils/get-request-ip.mjs";
3
- import { safeJSONParse } from "@better-auth/core/utils/json";
3
+ import { BetterAuthError } from "@better-auth/core/error";
4
4
  import { normalizePathname } from "@better-auth/core/utils/url";
5
5
  import { createRateLimitKey } from "@better-auth/core/utils/ip";
6
6
  //#region src/api/rate-limiter/index.ts
7
7
  const memory = /* @__PURE__ */ new Map();
8
- function shouldRateLimit(max, window, rateLimitData) {
8
+ const MEMORY_STORE_MAX_ENTRIES = 1e5;
9
+ function pruneMemoryStore() {
9
10
  const now = Date.now();
10
- const windowInMs = window * 1e3;
11
- return now - rateLimitData.lastRequest < windowInMs && rateLimitData.count >= max;
11
+ for (const [key, entry] of memory) if (now >= entry.expiresAt) memory.delete(key);
12
+ if (memory.size <= MEMORY_STORE_MAX_ENTRIES) return;
13
+ const overflow = memory.size - MEMORY_STORE_MAX_ENTRIES;
14
+ let removed = 0;
15
+ for (const key of memory.keys()) {
16
+ memory.delete(key);
17
+ if (++removed >= overflow) break;
18
+ }
19
+ }
20
+ /**
21
+ * Decide an atomic rate-limit step against an in-memory `RateLimit` snapshot
22
+ * for the rolling `window` (seconds) and `max`. Shared by the memory backend
23
+ * (read-decide-write is atomic under single-threaded JS) and as the fallback
24
+ * for storages lacking an atomic primitive.
25
+ */
26
+ function decideConsume(data, rule, now) {
27
+ const windowInMs = rule.window * 1e3;
28
+ if (!data) return {
29
+ next: {
30
+ key: "",
31
+ count: 1,
32
+ lastRequest: now
33
+ },
34
+ update: false,
35
+ allowed: true,
36
+ retryAfter: null
37
+ };
38
+ if (now - data.lastRequest >= windowInMs) return {
39
+ next: {
40
+ ...data,
41
+ count: 1,
42
+ lastRequest: now
43
+ },
44
+ update: true,
45
+ allowed: true,
46
+ retryAfter: null
47
+ };
48
+ if (data.count >= rule.max) return {
49
+ next: data,
50
+ update: true,
51
+ allowed: false,
52
+ retryAfter: getRetryAfter(data.lastRequest, rule.window)
53
+ };
54
+ return {
55
+ next: {
56
+ ...data,
57
+ count: data.count + 1,
58
+ lastRequest: now
59
+ },
60
+ update: true,
61
+ allowed: true,
62
+ retryAfter: null
63
+ };
12
64
  }
13
65
  function rateLimitResponse(retryAfter) {
14
66
  return new Response(JSON.stringify({ message: "Too many requests. Please try again later." }), {
@@ -25,94 +77,173 @@ function getRetryAfter(lastRequest, window) {
25
77
  function createDatabaseStorageWrapper(ctx) {
26
78
  const model = "rateLimit";
27
79
  const db = ctx.adapter;
28
- return {
29
- get: async (key) => {
30
- const data = (await db.findMany({
80
+ let longestObservedWindow = Math.max(...getConfiguredRateLimitWindows(ctx));
81
+ const readRow = async (key) => {
82
+ const data = (await db.findMany({
83
+ model,
84
+ where: [{
85
+ field: "key",
86
+ value: key
87
+ }]
88
+ }))[0];
89
+ if (typeof data?.lastRequest === "bigint") data.lastRequest = Number(data.lastRequest);
90
+ return data;
91
+ };
92
+ const consume = async (key, rule) => {
93
+ if (rule.window > longestObservedWindow) longestObservedWindow = rule.window;
94
+ const windowInMs = rule.window * 1e3;
95
+ const data = await readRow(key);
96
+ const now = Date.now();
97
+ if (!data) try {
98
+ await db.create({
99
+ model,
100
+ data: {
101
+ key,
102
+ count: 1,
103
+ lastRequest: now
104
+ }
105
+ });
106
+ return {
107
+ allowed: true,
108
+ retryAfter: null
109
+ };
110
+ } catch (error) {
111
+ if (!await readRow(key)) throw error;
112
+ return consume(key, rule);
113
+ }
114
+ if (now - data.lastRequest >= windowInMs) {
115
+ if (await db.incrementOne({
31
116
  model,
32
117
  where: [{
33
118
  field: "key",
34
119
  value: key
35
- }]
36
- }))[0];
37
- if (typeof data?.lastRequest === "bigint") data.lastRequest = Number(data.lastRequest);
38
- return data;
39
- },
40
- set: async (key, value, _update) => {
41
- try {
42
- if (_update) await db.updateMany({
43
- model,
44
- where: [{
45
- field: "key",
46
- value: key
47
- }],
48
- update: {
49
- count: value.count,
50
- lastRequest: value.lastRequest
51
- }
52
- });
53
- else await db.create({
54
- model,
55
- data: {
56
- key,
57
- count: value.count,
58
- lastRequest: value.lastRequest
59
- }
60
- });
61
- } catch (e) {
62
- ctx.logger.error("Error setting rate limit", e);
120
+ }, {
121
+ field: "lastRequest",
122
+ operator: "lte",
123
+ value: data.lastRequest
124
+ }],
125
+ increment: {},
126
+ set: {
127
+ count: 1,
128
+ lastRequest: now
129
+ }
130
+ })) {
131
+ deleteExpiredRows(now);
132
+ return {
133
+ allowed: true,
134
+ retryAfter: null
135
+ };
63
136
  }
137
+ return consume(key, rule);
64
138
  }
139
+ const windowStart = now - windowInMs;
140
+ if (await db.incrementOne({
141
+ model,
142
+ where: [
143
+ {
144
+ field: "key",
145
+ value: key
146
+ },
147
+ {
148
+ field: "lastRequest",
149
+ operator: "gt",
150
+ value: windowStart
151
+ },
152
+ {
153
+ field: "count",
154
+ operator: "lt",
155
+ value: rule.max
156
+ }
157
+ ],
158
+ increment: { count: 1 },
159
+ set: { lastRequest: now }
160
+ })) return {
161
+ allowed: true,
162
+ retryAfter: null
163
+ };
164
+ const fresh = await readRow(key);
165
+ if (!fresh) return consume(key, rule);
166
+ if (now - fresh.lastRequest >= windowInMs) return consume(key, rule);
167
+ return {
168
+ allowed: false,
169
+ retryAfter: getRetryAfter(fresh.lastRequest, rule.window)
170
+ };
171
+ };
172
+ const deleteExpiredRows = (now) => {
173
+ const cutoff = now - longestObservedWindow * 1e3;
174
+ ctx.runInBackground(db.deleteMany({
175
+ model,
176
+ where: [{
177
+ field: "lastRequest",
178
+ operator: "lt",
179
+ value: cutoff
180
+ }]
181
+ }).then(() => void 0).catch((e) => ctx.logger.error("Error pruning rate limit rows", e)));
65
182
  };
183
+ return { consume };
184
+ }
185
+ function getConfiguredRateLimitWindows(ctx) {
186
+ const windows = [ctx.rateLimit.window, ...getDefaultSpecialRules().map((rule) => rule.window)];
187
+ for (const plugin of ctx.options.plugins || []) if (plugin.rateLimit) windows.push(...plugin.rateLimit.map((rule) => rule.window));
188
+ if (ctx.rateLimit.customRules) {
189
+ for (const customRule of Object.values(ctx.rateLimit.customRules)) if (customRule && typeof customRule !== "function") windows.push(customRule.window);
190
+ }
191
+ const validWindows = windows.filter((window) => Number.isFinite(window) && window > 0);
192
+ return validWindows.length > 0 ? validWindows : [ctx.rateLimit.window];
66
193
  }
67
194
  function getRateLimitStorage(ctx, rateLimitSettings) {
68
195
  if (ctx.options.rateLimit?.customStorage) return ctx.options.rateLimit.customStorage;
69
196
  const storage = ctx.rateLimit.storage;
70
- if (storage === "secondary-storage") return {
71
- get: async (key) => {
72
- const data = await ctx.options.secondaryStorage?.get(key);
73
- return data ? safeJSONParse(data) : null;
74
- },
75
- set: async (key, value, _update) => {
76
- const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10;
77
- await ctx.options.secondaryStorage?.set?.(key, JSON.stringify(value), ttl);
78
- }
79
- };
80
- else if (storage === "memory") return {
81
- async get(key) {
197
+ if (storage === "secondary-storage") {
198
+ const ttlFor = (window) => window ?? ctx.options.rateLimit?.window ?? 10;
199
+ const increment = ctx.options.secondaryStorage?.increment;
200
+ if (!increment) throw new BetterAuthError("Secondary-storage rate limiting requires SecondaryStorage.increment.");
201
+ return { consume: async (key, rule) => {
202
+ if (await increment(key, ttlFor(rule.window)) <= rule.max) return {
203
+ allowed: true,
204
+ retryAfter: null
205
+ };
206
+ return {
207
+ allowed: false,
208
+ retryAfter: rule.window
209
+ };
210
+ } };
211
+ } else if (storage === "memory") {
212
+ const ttlFor = (window) => window ?? ctx.options.rateLimit?.window ?? 10;
213
+ return { async consume(key, rule) {
214
+ pruneMemoryStore();
215
+ const now = Date.now();
82
216
  const entry = memory.get(key);
83
- if (!entry) return null;
84
- if (Date.now() >= entry.expiresAt) {
85
- memory.delete(key);
86
- return null;
87
- }
88
- return entry.data;
89
- },
90
- async set(key, value, _update) {
91
- const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10;
92
- const expiresAt = Date.now() + ttl * 1e3;
93
- memory.set(key, {
94
- data: value,
95
- expiresAt
217
+ const decision = decideConsume(entry && now < entry.expiresAt ? entry.data : void 0, rule, now);
218
+ if (decision.allowed) memory.set(key, {
219
+ data: {
220
+ ...decision.next,
221
+ key
222
+ },
223
+ expiresAt: now + ttlFor(rule.window) * 1e3
96
224
  });
97
- }
98
- };
225
+ return {
226
+ allowed: decision.allowed,
227
+ retryAfter: decision.retryAfter
228
+ };
229
+ } };
230
+ }
99
231
  return createDatabaseStorageWrapper(ctx);
100
232
  }
101
233
  let ipWarningLogged = false;
234
+ const NO_TRUSTED_IP_KEY = "no-trusted-ip";
102
235
  async function resolveRateLimitConfig(req, ctx) {
103
236
  const basePath = new URL(ctx.baseURL).pathname;
104
237
  const path = normalizePathname(req.url, basePath);
105
238
  let currentWindow = ctx.rateLimit.window;
106
239
  let currentMax = ctx.rateLimit.max;
107
240
  const ip = getIp(req, ctx.options);
108
- if (!ip) {
109
- if (!ipWarningLogged) {
110
- ctx.logger.warn("Rate limiting skipped: could not determine client IP address. Ensure your runtime forwards a trusted client IP header and configure `advanced.ipAddress.ipAddressHeaders` if needed.");
111
- ipWarningLogged = true;
112
- }
113
- return null;
241
+ if (!ip && ctx.options.advanced?.ipAddress?.disableIpTracking) return null;
242
+ if (!ip && !ipWarningLogged) {
243
+ ctx.logger.warn("Rate limiting could not determine a client IP and is falling back to a single shared per-path bucket. Ensure your runtime forwards a trusted client IP header and configure `advanced.ipAddress.ipAddressHeaders` if needed.");
244
+ ipWarningLogged = true;
114
245
  }
115
- const key = createRateLimitKey(ip, path);
246
+ const key = createRateLimitKey(ip ?? NO_TRUSTED_IP_KEY, path);
116
247
  const specialRule = getDefaultSpecialRules().find((rule) => rule.pathMatcher(path));
117
248
  if (specialRule) {
118
249
  currentWindow = specialRule.window;
@@ -150,37 +281,24 @@ async function resolveRateLimitConfig(req, ctx) {
150
281
  currentMax
151
282
  };
152
283
  }
284
+ /**
285
+ * Decides the rate limit for the request in a single atomic step. The whole
286
+ * check-and-increment happens here in the request phase; there is no separate
287
+ * response-phase write-back, so concurrent requests cannot all pass a stale
288
+ * read before any increment lands.
289
+ */
153
290
  async function onRequestRateLimit(req, ctx) {
154
291
  if (!ctx.rateLimit.enabled) return;
155
292
  const config = await resolveRateLimitConfig(req, ctx);
156
293
  if (!config) return;
157
294
  const { key, currentWindow, currentMax } = config;
158
- const data = await getRateLimitStorage(ctx, { window: currentWindow }).get(key);
159
- if (data && shouldRateLimit(currentMax, currentWindow, data)) return rateLimitResponse(getRetryAfter(data.lastRequest, currentWindow));
160
- }
161
- async function onResponseRateLimit(req, ctx) {
162
- if (!ctx.rateLimit.enabled) return;
163
- const config = await resolveRateLimitConfig(req, ctx);
164
- if (!config) return;
165
- const { key, currentWindow } = config;
166
295
  const storage = getRateLimitStorage(ctx, { window: currentWindow });
167
- const data = await storage.get(key);
168
- const now = Date.now();
169
- if (!data) await storage.set(key, {
170
- key,
171
- count: 1,
172
- lastRequest: now
173
- });
174
- else if (now - data.lastRequest > currentWindow * 1e3) await storage.set(key, {
175
- ...data,
176
- count: 1,
177
- lastRequest: now
178
- }, true);
179
- else await storage.set(key, {
180
- ...data,
181
- count: data.count + 1,
182
- lastRequest: now
183
- }, true);
296
+ const rule = {
297
+ window: currentWindow,
298
+ max: currentMax
299
+ };
300
+ const { allowed, retryAfter } = await storage.consume(key, rule);
301
+ if (!allowed) return rateLimitResponse(retryAfter ?? currentWindow);
184
302
  }
185
303
  function getDefaultSpecialRules() {
186
304
  return [{
@@ -198,4 +316,4 @@ function getDefaultSpecialRules() {
198
316
  }];
199
317
  }
200
318
  //#endregion
201
- export { onRequestRateLimit, onResponseRateLimit };
319
+ export { onRequestRateLimit };
@@ -1,3 +1,4 @@
1
+ import { shouldBindAccountCookieToSessionUser } from "../../context/store-capabilities.mjs";
1
2
  import { parseAccountOutput } from "../../db/schema.mjs";
2
3
  import { generateRandomString } from "../../crypto/random.mjs";
3
4
  import { getAccountCookie } from "../../cookies/session-store.mjs";
@@ -7,7 +8,7 @@ import { decryptOAuthToken } from "../../oauth2/token-encryption.mjs";
7
8
  import { persistOAuthAccount } from "../../oauth2/persist-account.mjs";
8
9
  import { applyUpdateUserInfoOnLink } from "../../oauth2/resolve-account.mjs";
9
10
  import { generateState } from "../../oauth2/state.mjs";
10
- import { freshSessionMiddleware, getSessionFromCtx, sessionMiddleware } from "./session.mjs";
11
+ import { freshSessionMiddleware, getSessionFromCtx, isStateful, sessionMiddleware } from "./session.mjs";
11
12
  import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
12
13
  import { additionalAuthorizationParamsSchema, readGrantedScopes, supportsIdTokenSignIn, verifyProviderIdToken } from "@better-auth/core/oauth2";
13
14
  import { SocialProviderListEnum } from "@better-auth/core/social-providers";
@@ -126,7 +127,7 @@ const linkSocialAccount = createAuthEndpoint("/link-social", {
126
127
  }
127
128
  const { token, nonce } = c.body.idToken;
128
129
  if (!await verifyProviderIdToken(provider, token, nonce)) {
129
- c.context.logger.error("Invalid id token", { provider: c.body.provider });
130
+ c.context.logger.warn("Invalid id token", { provider: c.body.provider });
130
131
  throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_TOKEN);
131
132
  }
132
133
  const linkingUserInfo = await provider.getUserInfo({
@@ -250,7 +251,7 @@ const unlinkAccount = createAuthEndpoint("/unlink-account", {
250
251
  * so the cache is left in place for them.
251
252
  */
252
253
  async function resolveUserId(ctx, userId) {
253
- const session = await getSessionFromCtx(ctx, { disableCookieCache: !!ctx.context.options.database || !!ctx.context.options.secondaryStorage });
254
+ const session = await getSessionFromCtx(ctx, { disableCookieCache: isStateful(ctx) });
254
255
  if (!session && (ctx.request || ctx.headers)) throw ctx.error("UNAUTHORIZED");
255
256
  const resolvedUserId = session?.user?.id || userId;
256
257
  if (!resolvedUserId) throw APIError.from("BAD_REQUEST", {
@@ -259,6 +260,9 @@ async function resolveUserId(ctx, userId) {
259
260
  });
260
261
  return resolvedUserId;
261
262
  }
263
+ function matchesAccountSelection(ctx, account, { resolvedUserId, providerId, accountId }) {
264
+ return (!shouldBindAccountCookieToSessionUser(ctx.context.options) || account.userId === resolvedUserId) && (!providerId || providerId === account.providerId) && (!accountId || account.accountId === accountId);
265
+ }
262
266
  /**
263
267
  * Fetches a currently-valid access token for a user's provider account,
264
268
  * refreshing and persisting it when it is within five seconds of expiry.
@@ -274,7 +278,11 @@ async function getValidAccessToken(ctx, { resolvedUserId, providerId, accountId,
274
278
  let account = resolvedAccount;
275
279
  if (!account) {
276
280
  const accountData = await getAccountCookie(ctx);
277
- if (accountData && accountData.userId === resolvedUserId && providerId === accountData.providerId && (!accountId || accountData.accountId === accountId)) account = accountData;
281
+ if (accountData && matchesAccountSelection(ctx, accountData, {
282
+ resolvedUserId,
283
+ providerId,
284
+ accountId
285
+ })) account = accountData;
278
286
  else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.accountId === accountId && acc.providerId === providerId : acc.providerId === providerId);
279
287
  }
280
288
  if (!account) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
@@ -401,7 +409,11 @@ const refreshToken = createAuthEndpoint("/refresh-token", {
401
409
  });
402
410
  let account = void 0;
403
411
  const accountData = await getAccountCookie(ctx);
404
- if (!!accountData && accountData.userId === resolvedUserId && providerId === accountData.providerId && (!accountId || accountData.accountId === accountId)) account = accountData;
412
+ if (!!accountData && matchesAccountSelection(ctx, accountData, {
413
+ resolvedUserId,
414
+ providerId,
415
+ accountId
416
+ })) account = accountData;
405
417
  else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.accountId === accountId && acc.providerId === providerId : acc.providerId === providerId);
406
418
  if (!account) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
407
419
  const refreshToken = account.refreshToken ?? void 0;
@@ -475,7 +487,10 @@ const accountInfo = createAuthEndpoint("/account-info", {
475
487
  if (!providedAccountId) {
476
488
  if (ctx.context.options.account?.storeAccountCookie) {
477
489
  const accountData = await getAccountCookie(ctx);
478
- if (accountData) account = accountData;
490
+ if (accountData && matchesAccountSelection(ctx, accountData, {
491
+ resolvedUserId,
492
+ providerId: providedProviderId
493
+ })) account = accountData;
479
494
  }
480
495
  } else {
481
496
  const matchingAccounts = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).filter((acc) => acc.accountId === providedAccountId && (!providedProviderId || acc.providerId === providedProviderId));
@@ -485,7 +500,7 @@ const accountInfo = createAuthEndpoint("/account-info", {
485
500
  });
486
501
  account = matchingAccounts[0];
487
502
  }
488
- if (!account || account.userId !== resolvedUserId) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
503
+ if (!account || !matchesAccountSelection(ctx, account, { resolvedUserId })) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
489
504
  const provider = await getAwaitableValue(ctx.context.socialProviders, { value: account.providerId });
490
505
  if (!provider) throw APIError.from("BAD_REQUEST", {
491
506
  message: "Account is not associated with a configured social provider.",
@@ -88,12 +88,12 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
88
88
  }
89
89
  if (error) redirectOnError(error, error_description);
90
90
  if (!code) {
91
- c.context.logger.error("Code not found");
91
+ c.context.logger.warn("Code not found");
92
92
  throw redirectOnError(OAUTH_CALLBACK_ERROR_CODES.NO_CODE);
93
93
  }
94
94
  const provider = await getAwaitableValue(c.context.socialProviders, { value: c.params.id });
95
95
  if (!provider) {
96
- c.context.logger.error("Oauth provider with id", c.params.id, "not found");
96
+ c.context.logger.warn("OAuth provider not found", { providerId: c.params.id });
97
97
  throw redirectOnError(OAUTH_CALLBACK_ERROR_CODES.PROVIDER_NOT_FOUND);
98
98
  }
99
99
  if (iss && provider.issuer && iss !== provider.issuer) {
@@ -27,11 +27,11 @@ async function sendVerificationEmailFn(ctx, user) {
27
27
  const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn);
28
28
  const callbackURL = ctx.body.callbackURL ? encodeURIComponent(ctx.body.callbackURL) : encodeURIComponent("/");
29
29
  const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`;
30
- await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({
30
+ await ctx.context.options.emailVerification.sendVerificationEmail({
31
31
  user,
32
32
  url,
33
33
  token
34
- }, ctx.request?.clone()));
34
+ }, ctx.request);
35
35
  }
36
36
  const sendVerificationEmail = createAuthEndpoint("/send-verification-email", {
37
37
  method: "POST",
@@ -94,12 +94,25 @@ const sendVerificationEmail = createAuthEndpoint("/send-verification-email", {
94
94
  const { email } = ctx.body;
95
95
  const session = await getSessionFromCtx(ctx);
96
96
  if (!session) {
97
+ /**
98
+ * Enforce a constant-time floor so an attacker cannot distinguish
99
+ * "email not found / already verified" (fast local JWT sign) from
100
+ * "email found and unverified" (slow external email-send) by
101
+ * comparing response times.
102
+ */
103
+ const MINIMUM_MS = 500;
104
+ const start = Date.now();
97
105
  const user = await ctx.context.internalAdapter.findUserByEmail(email);
98
- if (!user || user.user.emailVerified) {
99
- await createEmailVerificationToken(ctx.context.secret, email, void 0, ctx.context.options.emailVerification?.expiresIn);
100
- return ctx.json({ status: true });
106
+ let error;
107
+ if (!user || user.user.emailVerified) await createEmailVerificationToken(ctx.context.secret, email, void 0, ctx.context.options.emailVerification?.expiresIn);
108
+ else try {
109
+ await sendVerificationEmailFn(ctx, user.user);
110
+ } catch (e) {
111
+ error = e;
101
112
  }
102
- await sendVerificationEmailFn(ctx, user.user);
113
+ const remaining = MINIMUM_MS - (Date.now() - start);
114
+ if (remaining > 0) await new Promise((resolve) => setTimeout(resolve, remaining));
115
+ if (error) throw error;
103
116
  return ctx.json({ status: true });
104
117
  }
105
118
  if (session?.user.email.toLowerCase() !== email.toLowerCase()) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.EMAIL_MISMATCH);
@@ -4,7 +4,7 @@ import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEm
4
4
  import { error } from "./error.mjs";
5
5
  import { ok } from "./ok.mjs";
6
6
  import { requestPasswordReset, requestPasswordResetCallback, resetPassword, verifyPassword } from "./password.mjs";
7
- import { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs";
7
+ import { freshSessionMiddleware, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs";
8
8
  import { signInEmail, signInSocial } from "./sign-in.mjs";
9
9
  import { signOut } from "./sign-out.mjs";
10
10
  import { signUpEmail } from "./sign-up.mjs";