better-auth 1.7.0-beta.5 → 1.7.0-beta.7
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 +29 -11
- package/dist/api/routes/callback.mjs +13 -5
- 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 +10 -7
- 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/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/oauth2/errors.mjs +1 -0
- package/dist/oauth2/index.d.mts +2 -2
- package/dist/oauth2/index.mjs +2 -2
- package/dist/oauth2/state.d.mts +14 -1
- package/dist/oauth2/state.mjs +13 -2
- 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 +26 -17
- package/dist/plugins/generic-oauth/types.d.mts +43 -4
- 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 +232 -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.d.mts +6 -0
- package/dist/state.mjs +3 -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/utils/index.d.mts +1 -1
- 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";
|
|
@@ -6,8 +7,8 @@ import { missingEmailLogMessage } from "../../oauth2/errors.mjs";
|
|
|
6
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
|
-
import { generateState } from "../../oauth2/state.mjs";
|
|
10
|
-
import { freshSessionMiddleware, getSessionFromCtx, sessionMiddleware } from "./session.mjs";
|
|
10
|
+
import { generateIdTokenNonce, generateState } from "../../oauth2/state.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({
|
|
@@ -183,9 +184,11 @@ const linkSocialAccount = createAuthEndpoint("/link-social", {
|
|
|
183
184
|
}
|
|
184
185
|
const stateNonce = generateRandomString(32);
|
|
185
186
|
const codeVerifier = generateRandomString(128);
|
|
187
|
+
const idTokenNonce = generateIdTokenNonce(provider);
|
|
186
188
|
const { url, requestedScopes } = await provider.createAuthorizationURL({
|
|
187
189
|
state: stateNonce,
|
|
188
190
|
codeVerifier,
|
|
191
|
+
idTokenNonce,
|
|
189
192
|
redirectURI: `${c.context.baseURL}${provider.callbackPath}`,
|
|
190
193
|
scopes: c.body.scopes,
|
|
191
194
|
loginHint: c.body.loginHint,
|
|
@@ -199,7 +202,8 @@ const linkSocialAccount = createAuthEndpoint("/link-social", {
|
|
|
199
202
|
additionalData: c.body.additionalData,
|
|
200
203
|
requestedScopes,
|
|
201
204
|
state: stateNonce,
|
|
202
|
-
codeVerifier
|
|
205
|
+
codeVerifier,
|
|
206
|
+
idTokenNonce
|
|
203
207
|
});
|
|
204
208
|
if (!c.body.disableRedirect) c.setHeader("Location", url.toString());
|
|
205
209
|
return c.json({
|
|
@@ -250,7 +254,7 @@ const unlinkAccount = createAuthEndpoint("/unlink-account", {
|
|
|
250
254
|
* so the cache is left in place for them.
|
|
251
255
|
*/
|
|
252
256
|
async function resolveUserId(ctx, userId) {
|
|
253
|
-
const session = await getSessionFromCtx(ctx, { disableCookieCache:
|
|
257
|
+
const session = await getSessionFromCtx(ctx, { disableCookieCache: isStateful(ctx) });
|
|
254
258
|
if (!session && (ctx.request || ctx.headers)) throw ctx.error("UNAUTHORIZED");
|
|
255
259
|
const resolvedUserId = session?.user?.id || userId;
|
|
256
260
|
if (!resolvedUserId) throw APIError.from("BAD_REQUEST", {
|
|
@@ -259,6 +263,9 @@ async function resolveUserId(ctx, userId) {
|
|
|
259
263
|
});
|
|
260
264
|
return resolvedUserId;
|
|
261
265
|
}
|
|
266
|
+
function matchesAccountSelection(ctx, account, { resolvedUserId, providerId, accountId }) {
|
|
267
|
+
return (!shouldBindAccountCookieToSessionUser(ctx.context.options) || account.userId === resolvedUserId) && (!providerId || providerId === account.providerId) && (!accountId || account.accountId === accountId);
|
|
268
|
+
}
|
|
262
269
|
/**
|
|
263
270
|
* Fetches a currently-valid access token for a user's provider account,
|
|
264
271
|
* refreshing and persisting it when it is within five seconds of expiry.
|
|
@@ -274,7 +281,11 @@ async function getValidAccessToken(ctx, { resolvedUserId, providerId, accountId,
|
|
|
274
281
|
let account = resolvedAccount;
|
|
275
282
|
if (!account) {
|
|
276
283
|
const accountData = await getAccountCookie(ctx);
|
|
277
|
-
if (accountData &&
|
|
284
|
+
if (accountData && matchesAccountSelection(ctx, accountData, {
|
|
285
|
+
resolvedUserId,
|
|
286
|
+
providerId,
|
|
287
|
+
accountId
|
|
288
|
+
})) account = accountData;
|
|
278
289
|
else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.accountId === accountId && acc.providerId === providerId : acc.providerId === providerId);
|
|
279
290
|
}
|
|
280
291
|
if (!account) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
@@ -283,7 +294,7 @@ async function getValidAccessToken(ctx, { resolvedUserId, providerId, accountId,
|
|
|
283
294
|
const accessTokenExpired = account.accessTokenExpiresAt && new Date(account.accessTokenExpiresAt).getTime() - Date.now() < 5e3;
|
|
284
295
|
if (account.refreshToken && accessTokenExpired && provider.refreshAccessToken) {
|
|
285
296
|
const refreshToken = await decryptOAuthToken(account.refreshToken, ctx.context);
|
|
286
|
-
newTokens = await provider.refreshAccessToken(refreshToken);
|
|
297
|
+
newTokens = await provider.refreshAccessToken(refreshToken, ctx);
|
|
287
298
|
await persistOAuthAccount(ctx, {
|
|
288
299
|
userId: account.userId,
|
|
289
300
|
providerId: account.providerId,
|
|
@@ -401,14 +412,18 @@ const refreshToken = createAuthEndpoint("/refresh-token", {
|
|
|
401
412
|
});
|
|
402
413
|
let account = void 0;
|
|
403
414
|
const accountData = await getAccountCookie(ctx);
|
|
404
|
-
if (!!accountData &&
|
|
415
|
+
if (!!accountData && matchesAccountSelection(ctx, accountData, {
|
|
416
|
+
resolvedUserId,
|
|
417
|
+
providerId,
|
|
418
|
+
accountId
|
|
419
|
+
})) account = accountData;
|
|
405
420
|
else account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((acc) => accountId ? acc.accountId === accountId && acc.providerId === providerId : acc.providerId === providerId);
|
|
406
421
|
if (!account) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
407
422
|
const refreshToken = account.refreshToken ?? void 0;
|
|
408
423
|
if (!refreshToken) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.REFRESH_TOKEN_NOT_FOUND);
|
|
409
424
|
try {
|
|
410
425
|
const decryptedRefreshToken = await decryptOAuthToken(refreshToken, ctx.context);
|
|
411
|
-
const tokens = await provider.refreshAccessToken(decryptedRefreshToken);
|
|
426
|
+
const tokens = await provider.refreshAccessToken(decryptedRefreshToken, ctx);
|
|
412
427
|
await persistOAuthAccount(ctx, {
|
|
413
428
|
userId: account.userId,
|
|
414
429
|
providerId: account.providerId,
|
|
@@ -475,7 +490,10 @@ const accountInfo = createAuthEndpoint("/account-info", {
|
|
|
475
490
|
if (!providedAccountId) {
|
|
476
491
|
if (ctx.context.options.account?.storeAccountCookie) {
|
|
477
492
|
const accountData = await getAccountCookie(ctx);
|
|
478
|
-
if (accountData
|
|
493
|
+
if (accountData && matchesAccountSelection(ctx, accountData, {
|
|
494
|
+
resolvedUserId,
|
|
495
|
+
providerId: providedProviderId
|
|
496
|
+
})) account = accountData;
|
|
479
497
|
}
|
|
480
498
|
} else {
|
|
481
499
|
const matchingAccounts = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).filter((acc) => acc.accountId === providedAccountId && (!providedProviderId || acc.providerId === providedProviderId));
|
|
@@ -485,7 +503,7 @@ const accountInfo = createAuthEndpoint("/account-info", {
|
|
|
485
503
|
});
|
|
486
504
|
account = matchingAccounts[0];
|
|
487
505
|
}
|
|
488
|
-
if (!account || account
|
|
506
|
+
if (!account || !matchesAccountSelection(ctx, account, { resolvedUserId })) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
489
507
|
const provider = await getAwaitableValue(ctx.context.socialProviders, { value: account.providerId });
|
|
490
508
|
if (!provider) throw APIError.from("BAD_REQUEST", {
|
|
491
509
|
message: "Account is not associated with a configured social provider.",
|