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.
- package/dist/_virtual/_rolldown/runtime.mjs +1 -10
- package/dist/api/index.d.mts +3 -3
- package/dist/api/index.mjs +4 -5
- package/dist/api/middlewares/origin-check.mjs +5 -1
- package/dist/api/rate-limiter/index.mjs +213 -95
- package/dist/api/routes/account.mjs +22 -7
- package/dist/api/routes/callback.mjs +2 -2
- package/dist/api/routes/email-verification.mjs +19 -6
- package/dist/api/routes/index.d.mts +1 -1
- package/dist/api/routes/password.mjs +3 -4
- package/dist/api/routes/session.d.mts +12 -1
- package/dist/api/routes/session.mjs +94 -83
- package/dist/api/routes/sign-in.mjs +5 -5
- package/dist/api/routes/sign-up.mjs +2 -2
- package/dist/api/routes/update-session.mjs +2 -3
- package/dist/api/routes/update-user.mjs +10 -12
- package/dist/auth/base.mjs +11 -7
- package/dist/client/config.d.mts +88 -0
- package/dist/client/equality.d.mts +19 -0
- package/dist/client/equality.mjs +42 -0
- package/dist/client/index.d.mts +5 -4
- package/dist/client/index.mjs +2 -1
- package/dist/client/lynx/index.d.mts +21 -118
- package/dist/client/path-to-object.d.mts +5 -2
- package/dist/client/plugins/index.d.mts +4 -3
- package/dist/client/plugins/index.mjs +4 -2
- package/dist/client/query.d.mts +4 -3
- package/dist/client/query.mjs +27 -17
- package/dist/client/react/index.d.mts +21 -118
- package/dist/client/session-atom.d.mts +11 -0
- package/dist/client/session-atom.mjs +129 -4
- package/dist/client/session-refresh.d.mts +3 -18
- package/dist/client/session-refresh.mjs +38 -49
- package/dist/client/solid/index.d.mts +20 -112
- package/dist/client/svelte/index.d.mts +21 -118
- package/dist/client/types.d.mts +27 -16
- package/dist/client/vanilla.d.mts +20 -118
- package/dist/client/vue/index.d.mts +37 -145
- package/dist/context/create-context.mjs +14 -2
- package/dist/context/helpers.mjs +5 -4
- package/dist/context/store-capabilities.mjs +12 -0
- package/dist/cookies/index.d.mts +7 -0
- package/dist/cookies/index.mjs +49 -19
- package/dist/cookies/session-store.d.mts +0 -17
- package/dist/cookies/session-store.mjs +42 -51
- package/dist/db/internal-adapter.mjs +43 -13
- package/dist/{packages/better-auth/package.mjs → package.mjs} +1 -1
- package/dist/plugins/access/access.mjs +49 -19
- package/dist/plugins/admin/routes.mjs +10 -3
- package/dist/plugins/captcha/constants.mjs +8 -1
- package/dist/plugins/captcha/index.mjs +17 -13
- package/dist/plugins/captcha/types.d.mts +25 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -0
- package/dist/plugins/device-authorization/index.d.mts +3 -0
- package/dist/plugins/device-authorization/routes.mjs +123 -100
- package/dist/plugins/email-otp/index.mjs +1 -1
- package/dist/plugins/email-otp/otp-token.mjs +1 -1
- package/dist/plugins/email-otp/routes.mjs +23 -53
- package/dist/plugins/generic-oauth/index.d.mts +2 -2
- package/dist/plugins/generic-oauth/index.mjs +15 -11
- package/dist/plugins/generic-oauth/types.d.mts +8 -3
- package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
- package/dist/plugins/haveibeenpwned/index.mjs +5 -1
- package/dist/plugins/index.d.mts +7 -6
- package/dist/plugins/index.mjs +6 -5
- package/dist/plugins/jwt/adapter.mjs +21 -2
- package/dist/plugins/jwt/cookie-cache.mjs +117 -0
- package/dist/plugins/jwt/index.d.mts +8 -0
- package/dist/plugins/jwt/index.mjs +6 -6
- package/dist/plugins/jwt/schema.d.mts +8 -0
- package/dist/plugins/jwt/schema.mjs +8 -0
- package/dist/plugins/jwt/sign.d.mts +51 -2
- package/dist/plugins/jwt/sign.mjs +52 -6
- package/dist/plugins/jwt/types.d.mts +36 -0
- package/dist/plugins/jwt/utils.mjs +6 -3
- package/dist/plugins/last-login-method/client.d.mts +10 -0
- package/dist/plugins/last-login-method/client.mjs +4 -1
- package/dist/plugins/multi-session/index.mjs +7 -5
- package/dist/plugins/oauth-popup/client.d.mts +82 -0
- package/dist/plugins/oauth-popup/client.mjs +203 -0
- package/dist/plugins/oauth-popup/constants.d.mts +11 -0
- package/dist/plugins/oauth-popup/constants.mjs +11 -0
- package/dist/plugins/oauth-popup/error-codes.d.mts +11 -0
- package/dist/plugins/oauth-popup/error-codes.mjs +10 -0
- package/dist/plugins/oauth-popup/index.d.mts +67 -0
- package/dist/plugins/oauth-popup/index.mjs +228 -0
- package/dist/plugins/oauth-popup/types.d.mts +30 -0
- package/dist/plugins/oauth-proxy/index.mjs +3 -3
- package/dist/plugins/oauth-proxy/utils.mjs +16 -2
- package/dist/plugins/one-tap/client.mjs +12 -6
- package/dist/plugins/one-tap/index.d.mts +1 -2
- package/dist/plugins/one-tap/index.mjs +13 -8
- package/dist/plugins/one-time-token/index.mjs +1 -3
- package/dist/plugins/open-api/generator.d.mts +67 -58
- package/dist/plugins/open-api/generator.mjs +241 -107
- package/dist/plugins/open-api/index.d.mts +2 -2
- package/dist/plugins/organization/adapter.d.mts +33 -7
- package/dist/plugins/organization/adapter.mjs +219 -44
- package/dist/plugins/organization/client.d.mts +4 -4
- package/dist/plugins/organization/organization.mjs +16 -0
- package/dist/plugins/organization/routes/crud-access-control.mjs +1 -1
- package/dist/plugins/organization/routes/crud-invites.mjs +49 -33
- package/dist/plugins/organization/routes/crud-members.mjs +59 -6
- package/dist/plugins/organization/routes/crud-team.d.mts +5 -8
- package/dist/plugins/organization/routes/crud-team.mjs +40 -3
- package/dist/plugins/organization/schema.d.mts +14 -0
- package/dist/plugins/organization/types.d.mts +2 -2
- package/dist/plugins/phone-number/index.d.mts +12 -0
- package/dist/plugins/phone-number/index.mjs +2 -1
- package/dist/plugins/phone-number/routes.mjs +80 -44
- package/dist/plugins/siwe/index.mjs +2 -3
- package/dist/plugins/two-factor/backup-codes/index.mjs +5 -4
- package/dist/plugins/two-factor/otp/index.mjs +11 -13
- package/dist/plugins/two-factor/totp/index.mjs +1 -1
- package/dist/plugins/two-factor/verify-two-factor.mjs +6 -2
- package/dist/plugins/username/index.mjs +6 -6
- package/dist/state.mjs +2 -1
- package/dist/test-utils/http-test-instance.d.mts +9 -3
- package/dist/test-utils/http-test-instance.mjs +49 -8
- package/dist/test-utils/test-instance.d.mts +3 -16135
- package/dist/version.mjs +1 -1
- package/package.json +10 -35
- package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/index.mjs +0 -32
- package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/linux.mjs +0 -55
- package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/macos.mjs +0 -26
- package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/termux.mjs +0 -39
- package/dist/node_modules/.pnpm/clipboardy@4.0.0/node_modules/clipboardy/lib/windows.mjs +0 -19
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/chunks/prompt.mjs +0 -845
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/core.mjs +0 -386
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/index.mjs +0 -320
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/shared/consola.DRwqZj3T.mjs +0 -62
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/shared/consola.DXBYu-KD.mjs +0 -190
- package/dist/node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/utils.mjs +0 -2
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.mjs +0 -29
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.mjs +0 -42
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.mjs +0 -67
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.mjs +0 -23
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.mjs +0 -22
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.mjs +0 -36
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/adapters/node.d.mts +0 -292
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/adapters/node.mjs +0 -127
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/index.d.mts +0 -145
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.BQXMA5bH.d.mts +0 -298
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.By9qWDAI.mjs +0 -9
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.CipVM6lf.mjs +0 -3549
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.D9ehKjSh.mjs +0 -66
- package/dist/node_modules/.pnpm/crossws@0.3.5/node_modules/crossws/dist/shared/crossws.DfCzGthR.mjs +0 -227
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/index.mjs +0 -224
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/command.mjs +0 -52
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/error.mjs +0 -54
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/kill.mjs +0 -62
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/pipe.mjs +0 -26
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/promise.mjs +0 -32
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/stdio.mjs +0 -19
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/stream.mjs +0 -98
- package/dist/node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/verbose.mjs +0 -15
- package/dist/node_modules/.pnpm/get-port-please@3.2.0/node_modules/get-port-please/dist/index.d.mts +0 -15
- package/dist/node_modules/.pnpm/get-port-please@3.2.0/node_modules/get-port-please/dist/index.mjs +0 -220
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/array-buffer.mjs +0 -54
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/array.mjs +0 -2
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/buffer.mjs +0 -14
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/contents.mjs +0 -76
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/index.mjs +0 -6
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/string.mjs +0 -35
- package/dist/node_modules/.pnpm/get-stream@8.0.1/node_modules/get-stream/source/utils.mjs +0 -10
- package/dist/node_modules/.pnpm/http-shutdown@1.2.2/node_modules/http-shutdown/index.mjs +0 -76
- package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/core.mjs +0 -274
- package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/main.mjs +0 -44
- package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/realtime.mjs +0 -15
- package/dist/node_modules/.pnpm/human-signals@5.0.0/node_modules/human-signals/build/src/signals.mjs +0 -23
- package/dist/node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.mjs +0 -24
- package/dist/node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.mjs +0 -18
- package/dist/node_modules/.pnpm/is-stream@3.0.0/node_modules/is-stream/index.mjs +0 -9
- package/dist/node_modules/.pnpm/is-wsl@3.1.1/node_modules/is-wsl/index.mjs +0 -20
- package/dist/node_modules/.pnpm/is64bit@2.0.0/node_modules/is64bit/index.mjs +0 -13
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.mjs +0 -47
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.mjs +0 -33
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.mjs +0 -33
- package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/chunks/xdg-open.mjs +0 -1070
- package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/index.mjs +0 -619
- package/dist/node_modules/.pnpm/listhen@1.9.0/node_modules/listhen/dist/shared/listhen.1c46e31d.d.mts +0 -83
- package/dist/node_modules/.pnpm/merge-stream@2.0.0/node_modules/merge-stream/index.mjs +0 -38
- package/dist/node_modules/.pnpm/mimic-fn@4.0.0/node_modules/mimic-fn/index.mjs +0 -38
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/aes.mjs +0 -597
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/aesCipherSuites.mjs +0 -195
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/asn1-validator.mjs +0 -76
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/asn1.mjs +0 -967
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/baseN.mjs +0 -137
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/cipher.mjs +0 -186
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/cipherModes.mjs +0 -597
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/des.mjs +0 -1187
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/ed25519.mjs +0 -1029
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/forge.mjs +0 -15
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/hmac.mjs +0 -107
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/index.mjs +0 -66
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/jsbn.mjs +0 -1334
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/kem.mjs +0 -146
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/log.mjs +0 -241
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md.all.mjs +0 -24
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md.mjs +0 -18
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/md5.mjs +0 -324
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/mgf.mjs +0 -20
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/mgf1.mjs +0 -44
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/oids.mjs +0 -154
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pbe.mjs +0 -815
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pbkdf2.mjs +0 -125
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pem.mjs +0 -175
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs1.mjs +0 -200
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs12.mjs +0 -724
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs7.mjs +0 -642
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pkcs7asn1.mjs +0 -405
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pki.mjs +0 -101
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/prime.mjs +0 -193
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/prng.mjs +0 -290
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/pss.mjs +0 -141
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/random.mjs +0 -141
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/rc2.mjs +0 -538
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/rsa.mjs +0 -1309
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha1.mjs +0 -230
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha256.mjs +0 -267
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/sha512.mjs +0 -413
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/ssh.mjs +0 -194
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/tls.mjs +0 -3655
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/util.mjs +0 -2117
- package/dist/node_modules/.pnpm/node-forge@1.4.0/node_modules/node-forge/lib/x509.mjs +0 -2168
- package/dist/node_modules/.pnpm/npm-run-path@5.3.0/node_modules/npm-run-path/index.mjs +0 -34
- package/dist/node_modules/.pnpm/onetime@6.0.0/node_modules/onetime/index.mjs +0 -26
- package/dist/node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.mjs +0 -14
- package/dist/node_modules/.pnpm/path-key@4.0.0/node_modules/path-key/index.mjs +0 -8
- package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.mjs +0 -176
- package/dist/node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.mjs +0 -17
- package/dist/node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.mjs +0 -8
- package/dist/node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/index.mjs +0 -169
- package/dist/node_modules/.pnpm/signal-exit@4.1.0/node_modules/signal-exit/dist/mjs/signals.mjs +0 -33
- package/dist/node_modules/.pnpm/std-env@3.10.0/node_modules/std-env/dist/index.mjs +0 -171
- package/dist/node_modules/.pnpm/strip-final-newline@3.0.0/node_modules/strip-final-newline/index.mjs +0 -10
- package/dist/node_modules/.pnpm/system-architecture@0.1.0/node_modules/system-architecture/index.mjs +0 -16
- package/dist/node_modules/.pnpm/uncrypto@0.1.3/node_modules/uncrypto/dist/crypto.node.mjs +0 -7
- package/dist/node_modules/.pnpm/untun@0.1.3/node_modules/untun/dist/chunks/index.mjs +0 -154
- package/dist/node_modules/.pnpm/untun@0.1.3/node_modules/untun/dist/index.mjs +0 -34
- package/dist/node_modules/.pnpm/uqr@0.1.2/node_modules/uqr/dist/index.mjs +0 -896
- package/dist/node_modules/.pnpm/which@2.0.2/node_modules/which/which.mjs +0 -76
- package/dist/plugins/mcp/authorize.mjs +0 -134
- package/dist/plugins/mcp/client/adapters.d.mts +0 -56
- package/dist/plugins/mcp/client/adapters.mjs +0 -117
- package/dist/plugins/mcp/client/index.d.mts +0 -44
- package/dist/plugins/mcp/client/index.mjs +0 -152
- package/dist/plugins/mcp/index.d.mts +0 -457
- package/dist/plugins/mcp/index.mjs +0 -770
- package/dist/plugins/oidc-provider/authorize.mjs +0 -204
- package/dist/plugins/oidc-provider/client.d.mts +0 -16
- package/dist/plugins/oidc-provider/client.mjs +0 -15
- package/dist/plugins/oidc-provider/error.mjs +0 -24
- package/dist/plugins/oidc-provider/index.d.mts +0 -705
- package/dist/plugins/oidc-provider/index.mjs +0 -1122
- package/dist/plugins/oidc-provider/schema.d.mts +0 -159
- package/dist/plugins/oidc-provider/schema.mjs +0 -129
- package/dist/plugins/oidc-provider/types.d.mts +0 -517
- package/dist/plugins/oidc-provider/utils/prompt.mjs +0 -16
- 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 {
|
|
27
|
+
export { __exportAll, __reExport };
|
package/dist/api/index.d.mts
CHANGED
|
@@ -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 };
|
package/dist/api/index.mjs
CHANGED
|
@@ -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
|
|
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) =>
|
|
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 {
|
|
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
|
-
|
|
8
|
+
const MEMORY_STORE_MAX_ENTRIES = 1e5;
|
|
9
|
+
function pruneMemoryStore() {
|
|
9
10
|
const now = Date.now();
|
|
10
|
-
const
|
|
11
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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")
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
|
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.
|
|
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:
|
|
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 &&
|
|
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 &&
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
30
|
+
await ctx.context.options.emailVerification.sendVerificationEmail({
|
|
31
31
|
user,
|
|
32
32
|
url,
|
|
33
33
|
token
|
|
34
|
-
}, ctx.request
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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";
|