@masheev/better-auth 0.0.0-stage → 1.7.5
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/LICENSE.md +20 -0
- package/README.md +31 -2
- package/TYPE-PATCH-NOTICE.md +9 -0
- package/TYPE-PATCH-PROVENANCE.json +267 -0
- package/dist/_virtual/_rolldown/runtime.mjs +27 -0
- package/dist/adapters/drizzle-adapter/index.d.mts +1 -0
- package/dist/adapters/drizzle-adapter/index.mjs +2 -0
- package/dist/adapters/index.d.mts +26 -0
- package/dist/adapters/index.mjs +9 -0
- package/dist/adapters/kysely-adapter/index.d.mts +1 -0
- package/dist/adapters/kysely-adapter/index.mjs +2 -0
- package/dist/adapters/memory-adapter/index.d.mts +1 -0
- package/dist/adapters/memory-adapter/index.mjs +2 -0
- package/dist/adapters/mongodb-adapter/index.d.mts +1 -0
- package/dist/adapters/mongodb-adapter/index.mjs +2 -0
- package/dist/adapters/prisma-adapter/index.d.mts +1 -0
- package/dist/adapters/prisma-adapter/index.mjs +2 -0
- package/dist/api/dispatch.d.mts +34 -0
- package/dist/api/dispatch.mjs +275 -0
- package/dist/api/index.d.mts +4053 -0
- package/dist/api/index.mjs +220 -0
- package/dist/api/middlewares/authorization.d.mts +69 -0
- package/dist/api/middlewares/authorization.mjs +78 -0
- package/dist/api/middlewares/index.d.mts +2 -0
- package/dist/api/middlewares/origin-check.d.mts +17 -0
- package/dist/api/middlewares/origin-check.mjs +155 -0
- package/dist/api/rate-limiter/index.mjs +318 -0
- package/dist/api/routes/account.d.mts +413 -0
- package/dist/api/routes/account.mjs +619 -0
- package/dist/api/routes/callback.d.mts +32 -0
- package/dist/api/routes/callback.mjs +261 -0
- package/dist/api/routes/email-verification.d.mts +160 -0
- package/dist/api/routes/email-verification.mjs +324 -0
- package/dist/api/routes/error.d.mts +27 -0
- package/dist/api/routes/error.mjs +387 -0
- package/dist/api/routes/index.d.mts +12 -0
- package/dist/api/routes/ok.d.mts +35 -0
- package/dist/api/routes/ok.mjs +27 -0
- package/dist/api/routes/password.d.mts +182 -0
- package/dist/api/routes/password.mjs +206 -0
- package/dist/api/routes/session.d.mts +450 -0
- package/dist/api/routes/session.mjs +481 -0
- package/dist/api/routes/sign-in.d.mts +186 -0
- package/dist/api/routes/sign-in.mjs +368 -0
- package/dist/api/routes/sign-out.d.mts +51 -0
- package/dist/api/routes/sign-out.mjs +96 -0
- package/dist/api/routes/sign-up.d.mts +159 -0
- package/dist/api/routes/sign-up.mjs +275 -0
- package/dist/api/routes/update-session.d.mts +74 -0
- package/dist/api/routes/update-session.mjs +57 -0
- package/dist/api/routes/update-user.d.mts +428 -0
- package/dist/api/routes/update-user.mjs +522 -0
- package/dist/api/state/oauth.d.mts +53 -0
- package/dist/api/state/oauth.mjs +38 -0
- package/dist/api/state/should-session-refresh.d.mts +12 -0
- package/dist/api/state/should-session-refresh.mjs +13 -0
- package/dist/api/to-auth-endpoints.mjs +60 -0
- package/dist/auth/base.mjs +66 -0
- package/dist/auth/full.d.mts +28 -0
- package/dist/auth/full.mjs +29 -0
- package/dist/auth/minimal.d.mts +10 -0
- package/dist/auth/minimal.mjs +11 -0
- package/dist/auth/trusted-origins.mjs +106 -0
- package/dist/client/broadcast-channel.d.mts +19 -0
- package/dist/client/broadcast-channel.mjs +44 -0
- package/dist/client/config.d.mts +90 -0
- package/dist/client/config.mjs +113 -0
- package/dist/client/equality.d.mts +19 -0
- package/dist/client/equality.mjs +42 -0
- package/dist/client/fetch-plugins.mjs +17 -0
- package/dist/client/focus-manager.d.mts +10 -0
- package/dist/client/focus-manager.mjs +30 -0
- package/dist/client/index.d.mts +35 -0
- package/dist/client/index.mjs +22 -0
- package/dist/client/lynx/index.d.mts +43 -0
- package/dist/client/lynx/index.mjs +22 -0
- package/dist/client/lynx/lynx-store.d.mts +46 -0
- package/dist/client/lynx/lynx-store.mjs +44 -0
- package/dist/client/online-manager.d.mts +11 -0
- package/dist/client/online-manager.mjs +33 -0
- package/dist/client/parser.d.mts +10 -0
- package/dist/client/parser.mjs +70 -0
- package/dist/client/path-to-object.d.mts +113 -0
- package/dist/client/plugins/index.d.mts +44 -0
- package/dist/client/plugins/index.mjs +30 -0
- package/dist/client/plugins/infer-plugin.d.mts +16 -0
- package/dist/client/plugins/infer-plugin.mjs +11 -0
- package/dist/client/proxy.mjs +78 -0
- package/dist/client/query.d.mts +23 -0
- package/dist/client/query.mjs +124 -0
- package/dist/client/react/index.d.mts +44 -0
- package/dist/client/react/index.mjs +22 -0
- package/dist/client/react/react-store.d.mts +46 -0
- package/dist/client/react/react-store.mjs +44 -0
- package/dist/client/session-atom.d.mts +11 -0
- package/dist/client/session-atom.mjs +178 -0
- package/dist/client/session-refresh.d.mts +20 -0
- package/dist/client/session-refresh.mjs +131 -0
- package/dist/client/solid/index.d.mts +44 -0
- package/dist/client/solid/index.mjs +22 -0
- package/dist/client/solid/solid-store.mjs +21 -0
- package/dist/client/svelte/index.d.mts +44 -0
- package/dist/client/svelte/index.mjs +18 -0
- package/dist/client/types.d.mts +51 -0
- package/dist/client/vanilla.d.mts +42 -0
- package/dist/client/vanilla.mjs +18 -0
- package/dist/client/vue/index.d.mts +67 -0
- package/dist/client/vue/index.mjs +54 -0
- package/dist/client/vue/vue-store.mjs +23 -0
- package/dist/context/create-context.mjs +235 -0
- package/dist/context/helpers.mjs +158 -0
- package/dist/context/init-minimal.mjs +17 -0
- package/dist/context/init.mjs +19 -0
- package/dist/context/secret-utils.mjs +53 -0
- package/dist/context/store-capabilities.mjs +12 -0
- package/dist/cookies/cache.mjs +33 -0
- package/dist/cookies/cookie-utils.d.mts +72 -0
- package/dist/cookies/cookie-utils.mjs +203 -0
- package/dist/cookies/index.d.mts +124 -0
- package/dist/cookies/index.mjs +350 -0
- package/dist/cookies/jwt.mjs +44 -0
- package/dist/cookies/session-store.d.mts +32 -0
- package/dist/cookies/session-store.mjs +183 -0
- package/dist/crypto/buffer.d.mts +7 -0
- package/dist/crypto/buffer.mjs +16 -0
- package/dist/crypto/index.d.mts +32 -0
- package/dist/crypto/index.mjs +69 -0
- package/dist/crypto/jwt.d.mts +9 -0
- package/dist/crypto/jwt.mjs +141 -0
- package/dist/crypto/password.d.mts +13 -0
- package/dist/crypto/password.mjs +14 -0
- package/dist/crypto/random.d.mts +4 -0
- package/dist/crypto/random.mjs +5 -0
- package/dist/db/adapter-base.d.mts +7 -0
- package/dist/db/adapter-base.mjs +25 -0
- package/dist/db/adapter-kysely.d.mts +7 -0
- package/dist/db/adapter-kysely.mjs +17 -0
- package/dist/db/field-converter.d.mts +7 -0
- package/dist/db/field-converter.mjs +19 -0
- package/dist/db/field.d.mts +41 -0
- package/dist/db/get-migration.d.mts +56 -0
- package/dist/db/get-migration.mjs +685 -0
- package/dist/db/get-schema.d.mts +13 -0
- package/dist/db/get-schema.mjs +39 -0
- package/dist/db/index.d.mts +10 -0
- package/dist/db/index.mjs +35 -0
- package/dist/db/internal-adapter.d.mts +14 -0
- package/dist/db/internal-adapter.mjs +951 -0
- package/dist/db/revoke-unproven-account-access.d.mts +21 -0
- package/dist/db/revoke-unproven-account-access.mjs +58 -0
- package/dist/db/schema.d.mts +62 -0
- package/dist/db/schema.mjs +161 -0
- package/dist/db/to-zod.d.mts +35 -0
- package/dist/db/to-zod.mjs +23 -0
- package/dist/db/verification-token-storage.mjs +25 -0
- package/dist/db/with-hooks.d.mts +37 -0
- package/dist/db/with-hooks.mjs +255 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.mjs +17 -0
- package/dist/integrations/cookie-plugin-guard.mjs +18 -0
- package/dist/integrations/next-js.d.mts +29 -0
- package/dist/integrations/next-js.mjs +105 -0
- package/dist/integrations/node.d.mts +11 -0
- package/dist/integrations/node.mjs +13 -0
- package/dist/integrations/solid-start.d.mts +22 -0
- package/dist/integrations/solid-start.mjs +15 -0
- package/dist/integrations/svelte-kit.d.mts +38 -0
- package/dist/integrations/svelte-kit.mjs +57 -0
- package/dist/integrations/tanstack-start-solid.d.mts +33 -0
- package/dist/integrations/tanstack-start-solid.mjs +57 -0
- package/dist/integrations/tanstack-start.d.mts +33 -0
- package/dist/integrations/tanstack-start.mjs +57 -0
- package/dist/oauth2/account-key.mjs +43 -0
- package/dist/oauth2/errors.mjs +49 -0
- package/dist/oauth2/index.d.mts +5 -0
- package/dist/oauth2/index.mjs +5 -0
- package/dist/oauth2/link-account.d.mts +96 -0
- package/dist/oauth2/link-account.mjs +329 -0
- package/dist/oauth2/state.d.mts +54 -0
- package/dist/oauth2/state.mjs +67 -0
- package/dist/oauth2/utils.d.mts +11 -0
- package/dist/oauth2/utils.mjs +33 -0
- package/dist/package.mjs +4 -0
- package/dist/plugins/access/access.d.mts +17 -0
- package/dist/plugins/access/access.mjs +78 -0
- package/dist/plugins/access/index.d.mts +3 -0
- package/dist/plugins/access/index.mjs +2 -0
- package/dist/plugins/access/types.d.mts +24 -0
- package/dist/plugins/additional-fields/client.d.mts +100 -0
- package/dist/plugins/additional-fields/client.mjs +11 -0
- package/dist/plugins/admin/access/index.d.mts +2 -0
- package/dist/plugins/admin/access/index.mjs +2 -0
- package/dist/plugins/admin/access/statement.d.mts +51 -0
- package/dist/plugins/admin/access/statement.mjs +52 -0
- package/dist/plugins/admin/admin.d.mts +911 -0
- package/dist/plugins/admin/admin.mjs +89 -0
- package/dist/plugins/admin/client.d.mts +81 -0
- package/dist/plugins/admin/client.mjs +39 -0
- package/dist/plugins/admin/error-codes.d.mts +30 -0
- package/dist/plugins/admin/error-codes.mjs +29 -0
- package/dist/plugins/admin/has-permission.mjs +12 -0
- package/dist/plugins/admin/index.d.mts +3 -0
- package/dist/plugins/admin/index.mjs +2 -0
- package/dist/plugins/admin/routes.mjs +919 -0
- package/dist/plugins/admin/schema.d.mts +40 -0
- package/dist/plugins/admin/schema.mjs +33 -0
- package/dist/plugins/admin/types.d.mts +88 -0
- package/dist/plugins/anonymous/client.d.mts +32 -0
- package/dist/plugins/anonymous/client.mjs +21 -0
- package/dist/plugins/anonymous/error-codes.d.mts +15 -0
- package/dist/plugins/anonymous/error-codes.mjs +14 -0
- package/dist/plugins/anonymous/index.d.mts +175 -0
- package/dist/plugins/anonymous/index.mjs +219 -0
- package/dist/plugins/anonymous/schema.d.mts +15 -0
- package/dist/plugins/anonymous/schema.mjs +9 -0
- package/dist/plugins/anonymous/types.d.mts +65 -0
- package/dist/plugins/bearer/index.d.mts +45 -0
- package/dist/plugins/bearer/index.mjs +74 -0
- package/dist/plugins/captcha/constants.d.mts +9 -0
- package/dist/plugins/captcha/constants.mjs +27 -0
- package/dist/plugins/captcha/error-codes.mjs +13 -0
- package/dist/plugins/captcha/index.d.mts +27 -0
- package/dist/plugins/captcha/index.mjs +79 -0
- package/dist/plugins/captcha/types.d.mts +52 -0
- package/dist/plugins/captcha/utils.mjs +9 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +27 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +47 -0
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +32 -0
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +27 -0
- package/dist/plugins/custom-session/client.d.mts +17 -0
- package/dist/plugins/custom-session/client.mjs +7 -0
- package/dist/plugins/custom-session/index.d.mts +73 -0
- package/dist/plugins/custom-session/index.mjs +65 -0
- package/dist/plugins/device-authorization/client.d.mts +17 -0
- package/dist/plugins/device-authorization/client.mjs +18 -0
- package/dist/plugins/device-authorization/error-codes.mjs +19 -0
- package/dist/plugins/device-authorization/index.d.mts +558 -0
- package/dist/plugins/device-authorization/index.mjs +87 -0
- package/dist/plugins/device-authorization/routes.d.mts +32 -0
- package/dist/plugins/device-authorization/routes.mjs +725 -0
- package/dist/plugins/device-authorization/schema.d.mts +18 -0
- package/dist/plugins/device-authorization/schema.mjs +63 -0
- package/dist/plugins/email-otp/client.d.mts +21 -0
- package/dist/plugins/email-otp/client.mjs +17 -0
- package/dist/plugins/email-otp/error-codes.d.mts +10 -0
- package/dist/plugins/email-otp/error-codes.mjs +9 -0
- package/dist/plugins/email-otp/index.d.mts +551 -0
- package/dist/plugins/email-otp/index.mjs +145 -0
- package/dist/plugins/email-otp/otp-token.mjs +55 -0
- package/dist/plugins/email-otp/routes.mjs +784 -0
- package/dist/plugins/email-otp/types.d.mts +109 -0
- package/dist/plugins/email-otp/utils.mjs +17 -0
- package/dist/plugins/generic-oauth/error-codes.mjs +8 -0
- package/dist/plugins/generic-oauth/index.d.mts +52 -0
- package/dist/plugins/generic-oauth/index.mjs +279 -0
- package/dist/plugins/generic-oauth/providers/auth0.d.mts +36 -0
- package/dist/plugins/generic-oauth/providers/auth0.mjs +49 -0
- package/dist/plugins/generic-oauth/providers/gumroad.d.mts +31 -0
- package/dist/plugins/generic-oauth/providers/gumroad.mjs +62 -0
- package/dist/plugins/generic-oauth/providers/hubspot.d.mts +36 -0
- package/dist/plugins/generic-oauth/providers/hubspot.mjs +62 -0
- package/dist/plugins/generic-oauth/providers/index.d.mts +10 -0
- package/dist/plugins/generic-oauth/providers/keycloak.d.mts +36 -0
- package/dist/plugins/generic-oauth/providers/keycloak.mjs +47 -0
- package/dist/plugins/generic-oauth/providers/line.d.mts +54 -0
- package/dist/plugins/generic-oauth/providers/line.mjs +93 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.d.mts +39 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.mjs +104 -0
- package/dist/plugins/generic-oauth/providers/okta.d.mts +36 -0
- package/dist/plugins/generic-oauth/providers/okta.mjs +47 -0
- package/dist/plugins/generic-oauth/providers/patreon.d.mts +29 -0
- package/dist/plugins/generic-oauth/providers/patreon.mjs +61 -0
- package/dist/plugins/generic-oauth/providers/slack.d.mts +29 -0
- package/dist/plugins/generic-oauth/providers/slack.mjs +63 -0
- package/dist/plugins/generic-oauth/providers/yandex.d.mts +29 -0
- package/dist/plugins/generic-oauth/providers/yandex.mjs +67 -0
- package/dist/plugins/generic-oauth/types.d.mts +268 -0
- package/dist/plugins/haveibeenpwned/index.d.mts +63 -0
- package/dist/plugins/haveibeenpwned/index.mjs +81 -0
- package/dist/plugins/index.d.mts +69 -0
- package/dist/plugins/index.mjs +49 -0
- package/dist/plugins/jwt/adapter.mjs +68 -0
- package/dist/plugins/jwt/client.d.mts +41 -0
- package/dist/plugins/jwt/client.mjs +19 -0
- package/dist/plugins/jwt/cookie-cache.mjs +77 -0
- package/dist/plugins/jwt/index.d.mts +246 -0
- package/dist/plugins/jwt/index.mjs +214 -0
- package/dist/plugins/jwt/schema.d.mts +33 -0
- package/dist/plugins/jwt/schema.mjs +29 -0
- package/dist/plugins/jwt/sign.d.mts +128 -0
- package/dist/plugins/jwt/sign.mjs +130 -0
- package/dist/plugins/jwt/types.d.mts +256 -0
- package/dist/plugins/jwt/utils.d.mts +41 -0
- package/dist/plugins/jwt/utils.mjs +64 -0
- package/dist/plugins/jwt/verify.d.mts +11 -0
- package/dist/plugins/jwt/verify.mjs +45 -0
- package/dist/plugins/last-login-method/client.d.mts +48 -0
- package/dist/plugins/last-login-method/client.mjs +48 -0
- package/dist/plugins/last-login-method/index.d.mts +127 -0
- package/dist/plugins/last-login-method/index.mjs +98 -0
- package/dist/plugins/magic-link/client.d.mts +10 -0
- package/dist/plugins/magic-link/client.mjs +11 -0
- package/dist/plugins/magic-link/index.d.mts +213 -0
- package/dist/plugins/magic-link/index.mjs +208 -0
- package/dist/plugins/magic-link/utils.mjs +9 -0
- package/dist/plugins/multi-session/client.d.mts +19 -0
- package/dist/plugins/multi-session/client.mjs +19 -0
- package/dist/plugins/multi-session/error-codes.d.mts +8 -0
- package/dist/plugins/multi-session/error-codes.mjs +5 -0
- package/dist/plugins/multi-session/index.d.mts +212 -0
- package/dist/plugins/multi-session/index.mjs +217 -0
- 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.d.mts +130 -0
- package/dist/plugins/oauth-proxy/index.mjs +407 -0
- package/dist/plugins/oauth-proxy/utils.mjs +55 -0
- package/dist/plugins/one-tap/client.d.mts +174 -0
- package/dist/plugins/one-tap/client.mjs +208 -0
- package/dist/plugins/one-tap/index.d.mts +82 -0
- package/dist/plugins/one-tap/index.mjs +100 -0
- package/dist/plugins/one-time-token/client.d.mts +10 -0
- package/dist/plugins/one-time-token/client.mjs +11 -0
- package/dist/plugins/one-time-token/index.d.mts +132 -0
- package/dist/plugins/one-time-token/index.mjs +107 -0
- package/dist/plugins/one-time-token/utils.mjs +9 -0
- package/dist/plugins/open-api/generator.d.mts +126 -0
- package/dist/plugins/open-api/generator.mjs +557 -0
- package/dist/plugins/open-api/index.d.mts +97 -0
- package/dist/plugins/open-api/index.mjs +65 -0
- package/dist/plugins/open-api/logo.mjs +13 -0
- package/dist/plugins/organization/access/index.d.mts +2 -0
- package/dist/plugins/organization/access/index.mjs +2 -0
- package/dist/plugins/organization/access/statement.d.mts +113 -0
- package/dist/plugins/organization/access/statement.mjs +77 -0
- package/dist/plugins/organization/adapter.d.mts +815 -0
- package/dist/plugins/organization/adapter.mjs +813 -0
- package/dist/plugins/organization/call.mjs +15 -0
- package/dist/plugins/organization/client.d.mts +375 -0
- package/dist/plugins/organization/client.mjs +104 -0
- package/dist/plugins/organization/error-codes.d.mts +66 -0
- package/dist/plugins/organization/error-codes.mjs +65 -0
- package/dist/plugins/organization/has-permission.d.mts +17 -0
- package/dist/plugins/organization/has-permission.mjs +34 -0
- package/dist/plugins/organization/index.d.mts +6 -0
- package/dist/plugins/organization/index.mjs +4 -0
- package/dist/plugins/organization/organization.d.mts +420 -0
- package/dist/plugins/organization/organization.mjs +886 -0
- package/dist/plugins/organization/permission.d.mts +15 -0
- package/dist/plugins/organization/permission.mjs +14 -0
- package/dist/plugins/organization/routes/crud-access-control.d.mts +511 -0
- package/dist/plugins/organization/routes/crud-access-control.mjs +674 -0
- package/dist/plugins/organization/routes/crud-invites.d.mts +1154 -0
- package/dist/plugins/organization/routes/crud-invites.mjs +602 -0
- package/dist/plugins/organization/routes/crud-members.d.mts +1057 -0
- package/dist/plugins/organization/routes/crud-members.mjs +518 -0
- package/dist/plugins/organization/routes/crud-org.d.mts +918 -0
- package/dist/plugins/organization/routes/crud-org.mjs +455 -0
- package/dist/plugins/organization/routes/crud-team.d.mts +1238 -0
- package/dist/plugins/organization/routes/crud-team.mjs +766 -0
- package/dist/plugins/organization/schema.d.mts +390 -0
- package/dist/plugins/organization/schema.mjs +65 -0
- package/dist/plugins/organization/types.d.mts +691 -0
- package/dist/plugins/phone-number/client.d.mts +31 -0
- package/dist/plugins/phone-number/client.mjs +19 -0
- package/dist/plugins/phone-number/error-codes.d.mts +19 -0
- package/dist/plugins/phone-number/error-codes.mjs +18 -0
- package/dist/plugins/phone-number/index.d.mts +360 -0
- package/dist/plugins/phone-number/index.mjs +57 -0
- package/dist/plugins/phone-number/routes.mjs +529 -0
- package/dist/plugins/phone-number/schema.d.mts +22 -0
- package/dist/plugins/phone-number/schema.mjs +18 -0
- package/dist/plugins/phone-number/types.d.mts +116 -0
- package/dist/plugins/siwe/client.d.mts +14 -0
- package/dist/plugins/siwe/client.mjs +15 -0
- package/dist/plugins/siwe/index.d.mts +62 -0
- package/dist/plugins/siwe/index.mjs +272 -0
- package/dist/plugins/siwe/parse-message.mjs +60 -0
- package/dist/plugins/siwe/schema.d.mts +35 -0
- package/dist/plugins/siwe/schema.mjs +30 -0
- package/dist/plugins/siwe/types.d.mts +43 -0
- package/dist/plugins/test-utils/auth-helpers.mjs +33 -0
- package/dist/plugins/test-utils/cookie-builder.mjs +55 -0
- package/dist/plugins/test-utils/db-helpers.mjs +65 -0
- package/dist/plugins/test-utils/factories.mjs +38 -0
- package/dist/plugins/test-utils/index.d.mts +73 -0
- package/dist/plugins/test-utils/index.mjs +97 -0
- package/dist/plugins/test-utils/otp-sink.mjs +21 -0
- package/dist/plugins/test-utils/types.d.mts +51 -0
- package/dist/plugins/two-factor/backup-codes/index.d.mts +291 -0
- package/dist/plugins/two-factor/backup-codes/index.mjs +341 -0
- package/dist/plugins/two-factor/client.d.mts +76 -0
- package/dist/plugins/two-factor/client.mjs +41 -0
- package/dist/plugins/two-factor/constant.mjs +6 -0
- package/dist/plugins/two-factor/error-code.d.mts +20 -0
- package/dist/plugins/two-factor/error-code.mjs +19 -0
- package/dist/plugins/two-factor/index.d.mts +720 -0
- package/dist/plugins/two-factor/index.mjs +349 -0
- package/dist/plugins/two-factor/otp/index.d.mts +216 -0
- package/dist/plugins/two-factor/otp/index.mjs +252 -0
- package/dist/plugins/two-factor/schema.d.mts +59 -0
- package/dist/plugins/two-factor/schema.mjs +53 -0
- package/dist/plugins/two-factor/totp/index.d.mts +219 -0
- package/dist/plugins/two-factor/totp/index.mjs +230 -0
- package/dist/plugins/two-factor/types.d.mts +111 -0
- package/dist/plugins/two-factor/utils.mjs +9 -0
- package/dist/plugins/two-factor/verify-two-factor.mjs +205 -0
- package/dist/plugins/username/client.d.mts +38 -0
- package/dist/plugins/username/client.mjs +17 -0
- package/dist/plugins/username/error-codes.d.mts +16 -0
- package/dist/plugins/username/error-codes.mjs +15 -0
- package/dist/plugins/username/index.d.mts +326 -0
- package/dist/plugins/username/index.mjs +312 -0
- package/dist/plugins/username/schema.d.mts +50 -0
- package/dist/plugins/username/schema.mjs +25 -0
- package/dist/social-providers/index.d.mts +1 -0
- package/dist/social-providers/index.mjs +2 -0
- package/dist/state.d.mts +48 -0
- package/dist/state.mjs +149 -0
- package/dist/test-utils/headers.d.mts +8 -0
- package/dist/test-utils/headers.mjs +17 -0
- package/dist/test-utils/http-test-instance.d.mts +413 -0
- package/dist/test-utils/http-test-instance.mjs +107 -0
- package/dist/test-utils/index.d.mts +4 -0
- package/dist/test-utils/index.mjs +4 -0
- package/dist/test-utils/test-instance.d.mts +56 -0
- package/dist/test-utils/test-instance.mjs +242 -0
- package/dist/types/adapter.d.mts +2 -0
- package/dist/types/api.d.mts +28 -0
- package/dist/types/auth.d.mts +32 -0
- package/dist/types/helper.d.mts +26 -0
- package/dist/types/index.d.mts +10 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/models.d.mts +10 -0
- package/dist/types/plugins.d.mts +19 -0
- package/dist/utils/boolean.mjs +6 -0
- package/dist/utils/constants.mjs +4 -0
- package/dist/utils/date.mjs +6 -0
- package/dist/utils/hashing.mjs +18 -0
- package/dist/utils/hide-metadata.d.mts +6 -0
- package/dist/utils/hide-metadata.mjs +4 -0
- package/dist/utils/index.d.mts +4 -0
- package/dist/utils/is-api-error.d.mts +2 -0
- package/dist/utils/is-api-error.mjs +2 -0
- package/dist/utils/is-atom.mjs +6 -0
- package/dist/utils/is-promise.mjs +6 -0
- package/dist/utils/middleware-response.mjs +7 -0
- package/dist/utils/password.mjs +32 -0
- package/dist/utils/plugin-helper.mjs +14 -0
- package/dist/utils/request.mjs +23 -0
- package/dist/utils/shim.mjs +22 -0
- package/dist/utils/time.d.mts +48 -0
- package/dist/utils/time.mjs +98 -0
- package/dist/utils/url.d.mts +78 -0
- package/dist/utils/url.mjs +238 -0
- package/dist/utils/validate-user-info.mjs +44 -0
- package/dist/utils/wildcard.mjs +106 -0
- package/dist/version.mjs +5 -0
- package/package.json +595 -3
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { isAPIError } from "../utils/is-api-error.mjs";
|
|
2
|
+
import { requireOrgRole, requireResourceOwnership } from "./middlewares/authorization.mjs";
|
|
3
|
+
import { formCsrfMiddleware, originCheck, originCheckMiddleware } from "./middlewares/origin-check.mjs";
|
|
4
|
+
import { onRequestRateLimit } from "./rate-limiter/index.mjs";
|
|
5
|
+
import { addOAuthServerContext, getOAuthState } from "./state/oauth.mjs";
|
|
6
|
+
import { getShouldSkipSessionRefresh, setShouldSkipSessionRefresh } from "./state/should-session-refresh.mjs";
|
|
7
|
+
import { freshSessionMiddleware, getAuthoritativeSessionFromCtx, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
|
|
8
|
+
import { accountInfo, getAccessToken, linkSocialAccount, listUserAccounts, refreshToken, unlinkAccount } from "./routes/account.mjs";
|
|
9
|
+
import { callbackOAuth } from "./routes/callback.mjs";
|
|
10
|
+
import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEmailFn, verifyEmail } from "./routes/email-verification.mjs";
|
|
11
|
+
import { error } from "./routes/error.mjs";
|
|
12
|
+
import { ok } from "./routes/ok.mjs";
|
|
13
|
+
import { requestPasswordReset, requestPasswordResetCallback, resetPassword, verifyPassword } from "./routes/password.mjs";
|
|
14
|
+
import { signInEmail, signInSocial } from "./routes/sign-in.mjs";
|
|
15
|
+
import { signOut } from "./routes/sign-out.mjs";
|
|
16
|
+
import { signUpEmail } from "./routes/sign-up.mjs";
|
|
17
|
+
import { updateSession } from "./routes/update-session.mjs";
|
|
18
|
+
import { changeEmail, changePassword, deleteUser, deleteUserCallback, setPassword, updateUser } from "./routes/update-user.mjs";
|
|
19
|
+
import { dispatchAuthEndpoint } from "./dispatch.mjs";
|
|
20
|
+
import { toAuthEndpoints } from "./to-auth-endpoints.mjs";
|
|
21
|
+
import { logger } from "@better-auth/core/env";
|
|
22
|
+
import { APIError } from "@better-auth/core/error";
|
|
23
|
+
import { getIP } from "@better-auth/core/utils/ip";
|
|
24
|
+
import { ATTR_CONTEXT, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, createWithSpan } from "@better-auth/core/instrumentation";
|
|
25
|
+
import { normalizePathname } from "@better-auth/core/utils/url";
|
|
26
|
+
import { createRouter } from "better-call";
|
|
27
|
+
import { NO_STORE_HEADERS, createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
|
|
28
|
+
//#region src/api/index.ts
|
|
29
|
+
function checkEndpointConflicts(options, logger) {
|
|
30
|
+
const endpointRegistry = /* @__PURE__ */ new Map();
|
|
31
|
+
options.plugins?.forEach((plugin) => {
|
|
32
|
+
if (plugin.endpoints) {
|
|
33
|
+
for (const [key, endpoint] of Object.entries(plugin.endpoints)) if (endpoint && "path" in endpoint && typeof endpoint.path === "string") {
|
|
34
|
+
const path = endpoint.path;
|
|
35
|
+
let methods = [];
|
|
36
|
+
if (endpoint.options && "method" in endpoint.options) {
|
|
37
|
+
if (Array.isArray(endpoint.options.method)) methods = endpoint.options.method;
|
|
38
|
+
else if (typeof endpoint.options.method === "string") methods = [endpoint.options.method];
|
|
39
|
+
}
|
|
40
|
+
if (methods.length === 0) methods = ["*"];
|
|
41
|
+
if (!endpointRegistry.has(path)) endpointRegistry.set(path, []);
|
|
42
|
+
endpointRegistry.get(path).push({
|
|
43
|
+
pluginId: plugin.id,
|
|
44
|
+
endpointKey: key,
|
|
45
|
+
methods
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const conflicts = [];
|
|
51
|
+
for (const [path, entries] of endpointRegistry.entries()) if (entries.length > 1) {
|
|
52
|
+
const methodMap = /* @__PURE__ */ new Map();
|
|
53
|
+
let hasConflict = false;
|
|
54
|
+
for (const entry of entries) for (const method of entry.methods) {
|
|
55
|
+
if (!methodMap.has(method)) methodMap.set(method, []);
|
|
56
|
+
methodMap.get(method).push(entry.pluginId);
|
|
57
|
+
if (methodMap.get(method).length > 1) hasConflict = true;
|
|
58
|
+
if (method === "*" && entries.length > 1) hasConflict = true;
|
|
59
|
+
else if (method !== "*" && methodMap.has("*")) hasConflict = true;
|
|
60
|
+
}
|
|
61
|
+
if (hasConflict) {
|
|
62
|
+
const uniquePlugins = [...new Set(entries.map((e) => e.pluginId))];
|
|
63
|
+
const conflictingMethods = [];
|
|
64
|
+
for (const [method, plugins] of methodMap.entries()) if (plugins.length > 1 || method === "*" && entries.length > 1 || method !== "*" && methodMap.has("*")) conflictingMethods.push(method);
|
|
65
|
+
conflicts.push({
|
|
66
|
+
path,
|
|
67
|
+
plugins: uniquePlugins,
|
|
68
|
+
conflictingMethods
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (conflicts.length > 0) {
|
|
73
|
+
const conflictMessages = conflicts.map((conflict) => ` - "${conflict.path}" [${conflict.conflictingMethods.join(", ")}] used by plugins: ${conflict.plugins.join(", ")}`).join("\n");
|
|
74
|
+
logger.error(`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods:
|
|
75
|
+
${conflictMessages}
|
|
76
|
+
|
|
77
|
+
To resolve this, you can:
|
|
78
|
+
1. Use only one of the conflicting plugins
|
|
79
|
+
2. Configure the plugins to use different paths (if supported)
|
|
80
|
+
3. Ensure plugins use different HTTP methods for the same path
|
|
81
|
+
`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function getEndpoints(ctx, options) {
|
|
85
|
+
const withSpan = createWithSpan(options);
|
|
86
|
+
const pluginEndpoints = options.plugins?.reduce((acc, plugin) => {
|
|
87
|
+
return {
|
|
88
|
+
...acc,
|
|
89
|
+
...plugin.endpoints
|
|
90
|
+
};
|
|
91
|
+
}, {}) ?? {};
|
|
92
|
+
const middlewares = options.plugins?.map((plugin) => plugin.middlewares?.map((m) => {
|
|
93
|
+
const middleware = (async (context) => {
|
|
94
|
+
const authContext = await ctx;
|
|
95
|
+
return withSpan(`middleware ${m.path} ${plugin.id}`, {
|
|
96
|
+
[ATTR_HOOK_TYPE]: "middleware",
|
|
97
|
+
[ATTR_HTTP_ROUTE]: m.path,
|
|
98
|
+
[ATTR_CONTEXT]: `plugin:${plugin.id}`
|
|
99
|
+
}, () => m.middleware({
|
|
100
|
+
...context,
|
|
101
|
+
context: {
|
|
102
|
+
...authContext,
|
|
103
|
+
...context.context
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
107
|
+
middleware.options = m.middleware.options;
|
|
108
|
+
return {
|
|
109
|
+
path: m.path,
|
|
110
|
+
middleware
|
|
111
|
+
};
|
|
112
|
+
})).filter((plugin) => plugin !== void 0).flat() || [];
|
|
113
|
+
return {
|
|
114
|
+
api: toAuthEndpoints({
|
|
115
|
+
signInSocial: signInSocial(),
|
|
116
|
+
callbackOAuth,
|
|
117
|
+
getSession: getSession(),
|
|
118
|
+
signOut,
|
|
119
|
+
signUpEmail: signUpEmail(),
|
|
120
|
+
signInEmail: signInEmail(),
|
|
121
|
+
resetPassword,
|
|
122
|
+
verifyPassword,
|
|
123
|
+
verifyEmail,
|
|
124
|
+
sendVerificationEmail,
|
|
125
|
+
changeEmail,
|
|
126
|
+
changePassword,
|
|
127
|
+
setPassword,
|
|
128
|
+
updateSession: updateSession(),
|
|
129
|
+
updateUser: updateUser(),
|
|
130
|
+
deleteUser,
|
|
131
|
+
requestPasswordReset,
|
|
132
|
+
requestPasswordResetCallback,
|
|
133
|
+
listSessions: listSessions(),
|
|
134
|
+
revokeSession,
|
|
135
|
+
revokeSessions,
|
|
136
|
+
revokeOtherSessions,
|
|
137
|
+
linkSocialAccount,
|
|
138
|
+
listUserAccounts,
|
|
139
|
+
deleteUserCallback,
|
|
140
|
+
unlinkAccount,
|
|
141
|
+
refreshToken,
|
|
142
|
+
getAccessToken,
|
|
143
|
+
accountInfo,
|
|
144
|
+
...pluginEndpoints,
|
|
145
|
+
ok,
|
|
146
|
+
error
|
|
147
|
+
}, ctx),
|
|
148
|
+
middlewares
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const router = (ctx, options) => {
|
|
152
|
+
const withSpan = createWithSpan(options);
|
|
153
|
+
const { api, middlewares } = getEndpoints(ctx, options);
|
|
154
|
+
const basePath = new URL(ctx.baseURL).pathname;
|
|
155
|
+
return createRouter(api, {
|
|
156
|
+
routerContext: ctx,
|
|
157
|
+
openapi: { disabled: true },
|
|
158
|
+
basePath,
|
|
159
|
+
routerMiddleware: [{
|
|
160
|
+
path: "/**",
|
|
161
|
+
middleware: originCheckMiddleware
|
|
162
|
+
}, ...middlewares],
|
|
163
|
+
allowedMediaTypes: ["application/json"],
|
|
164
|
+
skipTrailingSlashes: options.advanced?.skipTrailingSlashes ?? false,
|
|
165
|
+
async onRequest(req) {
|
|
166
|
+
const disabledPaths = ctx.options.disabledPaths || [];
|
|
167
|
+
const normalizedPath = normalizePathname(req.url, basePath);
|
|
168
|
+
if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 });
|
|
169
|
+
const pendingSchemaCheck = ctx.checkSchema?.();
|
|
170
|
+
if (pendingSchemaCheck) await pendingSchemaCheck;
|
|
171
|
+
let currentRequest = req;
|
|
172
|
+
const rateLimitResponse = await onRequestRateLimit(currentRequest, ctx);
|
|
173
|
+
if (rateLimitResponse) return rateLimitResponse;
|
|
174
|
+
for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) {
|
|
175
|
+
const response = await withSpan(`onRequest ${plugin.id}`, {
|
|
176
|
+
[ATTR_HOOK_TYPE]: "onRequest",
|
|
177
|
+
[ATTR_CONTEXT]: `plugin:${plugin.id}`
|
|
178
|
+
}, () => plugin.onRequest(currentRequest, ctx));
|
|
179
|
+
if (response && "response" in response) return response.response;
|
|
180
|
+
if (response && "request" in response) currentRequest = response.request;
|
|
181
|
+
}
|
|
182
|
+
return currentRequest;
|
|
183
|
+
},
|
|
184
|
+
async onResponse(res, req) {
|
|
185
|
+
for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) {
|
|
186
|
+
const response = await withSpan(`onResponse ${plugin.id}`, {
|
|
187
|
+
[ATTR_HOOK_TYPE]: "onResponse",
|
|
188
|
+
[ATTR_CONTEXT]: `plugin:${plugin.id}`,
|
|
189
|
+
[ATTR_HTTP_RESPONSE_STATUS_CODE]: res.status
|
|
190
|
+
}, () => plugin.onResponse(res, ctx));
|
|
191
|
+
if (response) return response.response;
|
|
192
|
+
}
|
|
193
|
+
return res;
|
|
194
|
+
},
|
|
195
|
+
onError(e) {
|
|
196
|
+
if (isAPIError(e) && e.status === "FOUND") return;
|
|
197
|
+
if (options.onAPIError?.throw) throw e;
|
|
198
|
+
if (options.onAPIError?.onError) {
|
|
199
|
+
options.onAPIError.onError(e, ctx);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const optLogLevel = options.logger?.level;
|
|
203
|
+
const log = optLogLevel === "error" || optLogLevel === "warn" || optLogLevel === "debug" ? logger : void 0;
|
|
204
|
+
if (options.logger?.disabled !== true) {
|
|
205
|
+
if (e && typeof e === "object" && "message" in e && typeof e.message === "string") {
|
|
206
|
+
if (e.message.includes("no column") || e.message.includes("column") || e.message.includes("relation") || e.message.includes("table") || e.message.includes("does not exist")) {
|
|
207
|
+
ctx.logger?.error(e.message);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (isAPIError(e)) {
|
|
212
|
+
if (e.status === "INTERNAL_SERVER_ERROR") ctx.logger.error(e.status, e);
|
|
213
|
+
log?.error(e.message);
|
|
214
|
+
} else ctx.logger?.error(e && typeof e === "object" && "name" in e ? e.name : "", e);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
//#endregion
|
|
220
|
+
export { APIError, NO_STORE_HEADERS, accountInfo, addOAuthServerContext, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, dispatchAuthEndpoint, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getAuthoritativeSessionFromCtx, 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 };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as _$better_call0 from "better-call";
|
|
2
|
+
|
|
3
|
+
//#region src/api/middlewares/authorization.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Middleware that verifies the authenticated user owns a resource.
|
|
6
|
+
* Must be used after sessionMiddleware in the `use` array.
|
|
7
|
+
*
|
|
8
|
+
* Fetches the resource by ID from the request body or query,
|
|
9
|
+
* then verifies `resource[ownerField] === session.user.id`.
|
|
10
|
+
* Throws NOT_FOUND if the resource doesn't exist, FORBIDDEN if
|
|
11
|
+
* the user doesn't own it.
|
|
12
|
+
*
|
|
13
|
+
* The fetched resource is returned on `ctx.context.verifiedResource`
|
|
14
|
+
* so the handler can use it without a redundant DB query.
|
|
15
|
+
*/
|
|
16
|
+
declare function requireResourceOwnership(config: {
|
|
17
|
+
/** Database model name (e.g., "passkey", "apiKey") */model: string; /** Request parameter name containing the resource ID */
|
|
18
|
+
idParam: string; /** Where to find the ID: "body" or "query" */
|
|
19
|
+
idSource: "body" | "query"; /** Field on the resource that holds the owner's user ID. Default: "userId" */
|
|
20
|
+
ownerField?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Custom error to throw when the resource is not found.
|
|
23
|
+
* Accepts a `{ code, message }` from `defineErrorCodes`.
|
|
24
|
+
* Default: generic NOT_FOUND.
|
|
25
|
+
*/
|
|
26
|
+
notFoundError?: {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Custom error to throw when the user doesn't own the resource.
|
|
32
|
+
* Accepts a `{ code, message }` from `defineErrorCodes`.
|
|
33
|
+
* Default: generic FORBIDDEN.
|
|
34
|
+
*/
|
|
35
|
+
forbiddenError?: {
|
|
36
|
+
code: string;
|
|
37
|
+
message: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* HTTP status for ownership failures.
|
|
41
|
+
* @default "FORBIDDEN" (403)
|
|
42
|
+
*/
|
|
43
|
+
forbiddenStatus?: "FORBIDDEN" | "UNAUTHORIZED";
|
|
44
|
+
}): _$better_call0.Middleware<_$better_call0.MiddlewareOptions, (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<{
|
|
45
|
+
verifiedResource: {};
|
|
46
|
+
}>>;
|
|
47
|
+
/**
|
|
48
|
+
* Middleware that verifies the authenticated user is a member of a
|
|
49
|
+
* specific organization with one of the allowed roles.
|
|
50
|
+
* Must be used after sessionMiddleware in the `use` array.
|
|
51
|
+
*
|
|
52
|
+
* Looks up the member record by {userId, organizationId}, then checks
|
|
53
|
+
* the member's role against the allowed list.
|
|
54
|
+
*
|
|
55
|
+
* The verified member is returned on `ctx.context.verifiedMember`.
|
|
56
|
+
*/
|
|
57
|
+
declare function requireOrgRole(config: {
|
|
58
|
+
/** Request parameter name containing the organization ID */orgIdParam: string; /** Where to find the org ID: "body" or "query" */
|
|
59
|
+
orgIdSource: "body" | "query";
|
|
60
|
+
/**
|
|
61
|
+
* Roles that are authorized to proceed. If omitted or empty,
|
|
62
|
+
* any org membership is sufficient.
|
|
63
|
+
*/
|
|
64
|
+
allowedRoles?: string[];
|
|
65
|
+
}): _$better_call0.Middleware<_$better_call0.MiddlewareOptions, (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<{
|
|
66
|
+
verifiedMember: {};
|
|
67
|
+
}>>;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { requireOrgRole, requireResourceOwnership };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { APIError } from "@better-auth/core/error";
|
|
2
|
+
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
3
|
+
//#region src/api/middlewares/authorization.ts
|
|
4
|
+
/**
|
|
5
|
+
* Middleware that verifies the authenticated user owns a resource.
|
|
6
|
+
* Must be used after sessionMiddleware in the `use` array.
|
|
7
|
+
*
|
|
8
|
+
* Fetches the resource by ID from the request body or query,
|
|
9
|
+
* then verifies `resource[ownerField] === session.user.id`.
|
|
10
|
+
* Throws NOT_FOUND if the resource doesn't exist, FORBIDDEN if
|
|
11
|
+
* the user doesn't own it.
|
|
12
|
+
*
|
|
13
|
+
* The fetched resource is returned on `ctx.context.verifiedResource`
|
|
14
|
+
* so the handler can use it without a redundant DB query.
|
|
15
|
+
*/
|
|
16
|
+
function requireResourceOwnership(config) {
|
|
17
|
+
const ownerField = config.ownerField ?? "userId";
|
|
18
|
+
const forbiddenStatus = config.forbiddenStatus ?? "FORBIDDEN";
|
|
19
|
+
return createAuthMiddleware(async (ctx) => {
|
|
20
|
+
const session = ctx.context.session;
|
|
21
|
+
if (!session?.user?.id) throw new APIError("UNAUTHORIZED");
|
|
22
|
+
const resourceId = (config.idSource === "body" ? ctx.body : ctx.query)?.[config.idParam];
|
|
23
|
+
if (!resourceId) throw new APIError("BAD_REQUEST", { message: `Missing required parameter: ${config.idParam}` });
|
|
24
|
+
const resource = await ctx.context.adapter.findOne({
|
|
25
|
+
model: config.model,
|
|
26
|
+
where: [{
|
|
27
|
+
field: "id",
|
|
28
|
+
value: resourceId
|
|
29
|
+
}]
|
|
30
|
+
});
|
|
31
|
+
if (!resource) {
|
|
32
|
+
if (config.notFoundError) throw APIError.from("NOT_FOUND", config.notFoundError);
|
|
33
|
+
throw new APIError("NOT_FOUND");
|
|
34
|
+
}
|
|
35
|
+
if (resource[ownerField] !== session.user.id) {
|
|
36
|
+
if (config.forbiddenError) throw APIError.from(forbiddenStatus, config.forbiddenError);
|
|
37
|
+
throw new APIError(forbiddenStatus);
|
|
38
|
+
}
|
|
39
|
+
return { verifiedResource: resource };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Middleware that verifies the authenticated user is a member of a
|
|
44
|
+
* specific organization with one of the allowed roles.
|
|
45
|
+
* Must be used after sessionMiddleware in the `use` array.
|
|
46
|
+
*
|
|
47
|
+
* Looks up the member record by {userId, organizationId}, then checks
|
|
48
|
+
* the member's role against the allowed list.
|
|
49
|
+
*
|
|
50
|
+
* The verified member is returned on `ctx.context.verifiedMember`.
|
|
51
|
+
*/
|
|
52
|
+
function requireOrgRole(config) {
|
|
53
|
+
const parseMemberRoles = (role) => role.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
54
|
+
return createAuthMiddleware(async (ctx) => {
|
|
55
|
+
const session = ctx.context.session;
|
|
56
|
+
if (!session?.user?.id) throw new APIError("UNAUTHORIZED");
|
|
57
|
+
if (!ctx.context.hasPlugin("organization")) throw new APIError("BAD_REQUEST", { message: "Organization plugin is required for org role authorization" });
|
|
58
|
+
const organizationId = (config.orgIdSource === "body" ? ctx.body : ctx.query)?.[config.orgIdParam];
|
|
59
|
+
if (!organizationId) throw new APIError("BAD_REQUEST", { message: `Missing required parameter: ${config.orgIdParam}` });
|
|
60
|
+
const member = await ctx.context.adapter.findOne({
|
|
61
|
+
model: "member",
|
|
62
|
+
where: [{
|
|
63
|
+
field: "userId",
|
|
64
|
+
value: session.user.id
|
|
65
|
+
}, {
|
|
66
|
+
field: "organizationId",
|
|
67
|
+
value: organizationId
|
|
68
|
+
}]
|
|
69
|
+
});
|
|
70
|
+
if (!member) throw new APIError("FORBIDDEN", { message: "Not a member of this organization" });
|
|
71
|
+
if (config.allowedRoles?.length) {
|
|
72
|
+
if (!parseMemberRoles(member.role ?? "").some((role) => config.allowedRoles.includes(role))) throw new APIError("FORBIDDEN", { message: "Insufficient role for this operation" });
|
|
73
|
+
}
|
|
74
|
+
return { verifiedMember: member };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { requireOrgRole, requireResourceOwnership };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GenericEndpointContext } from "@better-auth/core";
|
|
2
|
+
import * as _$better_call0 from "better-call";
|
|
3
|
+
|
|
4
|
+
//#region src/api/middlewares/origin-check.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A middleware to validate callbackURL and origin against trustedOrigins.
|
|
7
|
+
* Also handles CSRF protection using Fetch Metadata for first-login scenarios.
|
|
8
|
+
*/
|
|
9
|
+
declare const originCheckMiddleware: _$better_call0.Middleware<_$better_call0.MiddlewareOptions, (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<void>>;
|
|
10
|
+
declare const originCheck: (getValue: (ctx: GenericEndpointContext) => string | string[]) => _$better_call0.Middleware<_$better_call0.MiddlewareOptions, (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<void>>;
|
|
11
|
+
/**
|
|
12
|
+
* Middleware for CSRF protection using Fetch Metadata headers.
|
|
13
|
+
* This prevents cross-site navigation login attacks while supporting progressive enhancement.
|
|
14
|
+
*/
|
|
15
|
+
declare const formCsrfMiddleware: _$better_call0.Middleware<_$better_call0.MiddlewareOptions, (inputContext: _$better_call0.MiddlewareInputContext<_$better_call0.MiddlewareOptions>) => Promise<void>>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { formCsrfMiddleware, originCheck, originCheckMiddleware };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { getBaseURL, getOrigin } from "../../utils/url.mjs";
|
|
2
|
+
import { matchesOriginPattern } from "../../auth/trusted-origins.mjs";
|
|
3
|
+
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
4
|
+
import { normalizePathname } from "@better-auth/core/utils/url";
|
|
5
|
+
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
6
|
+
import { deprecate } from "@better-auth/core/utils/deprecate";
|
|
7
|
+
//#region src/api/middlewares/origin-check.ts
|
|
8
|
+
/**
|
|
9
|
+
* Checks if CSRF should be skipped for backward compatibility.
|
|
10
|
+
* Previously, disableOriginCheck also disabled CSRF checks.
|
|
11
|
+
* This maintains that behavior when disableCSRFCheck isn't explicitly set.
|
|
12
|
+
* Only triggers for skipOriginCheck === true, not for path arrays.
|
|
13
|
+
*/
|
|
14
|
+
function shouldSkipCSRFForBackwardCompat(ctx) {
|
|
15
|
+
return ctx.context.skipOriginCheck === true && ctx.context.options.advanced?.disableCSRFCheck === void 0;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the origin check should be skipped for the current request.
|
|
19
|
+
* Handles both boolean (skip all) and array (skip specific paths) configurations.
|
|
20
|
+
*/
|
|
21
|
+
function shouldSkipOriginCheck(ctx) {
|
|
22
|
+
const skipOriginCheck = ctx.context.skipOriginCheck;
|
|
23
|
+
if (skipOriginCheck === true) return true;
|
|
24
|
+
if (Array.isArray(skipOriginCheck) && ctx.request) try {
|
|
25
|
+
const basePath = new URL(ctx.context.baseURL).pathname;
|
|
26
|
+
const currentPath = normalizePathname(ctx.request.url, basePath);
|
|
27
|
+
return skipOriginCheck.some((skipPath) => {
|
|
28
|
+
const normalizedSkipPath = skipPath.replace(/\/+$/, "");
|
|
29
|
+
return currentPath === normalizedSkipPath || currentPath.startsWith(`${normalizedSkipPath}/`);
|
|
30
|
+
});
|
|
31
|
+
} catch {}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Logs deprecation warning for users relying on coupled behavior.
|
|
36
|
+
* Only logs if user explicitly set disableOriginCheck (not test environment default).
|
|
37
|
+
*/
|
|
38
|
+
const logBackwardCompatWarning = deprecate(function logBackwardCompatWarning() {}, "disableOriginCheck: true currently also disables CSRF checks. In a future version, disableOriginCheck will ONLY disable URL validation. To keep CSRF disabled, add disableCSRFCheck: true to your config.");
|
|
39
|
+
/**
|
|
40
|
+
* A middleware to validate callbackURL and origin against trustedOrigins.
|
|
41
|
+
* Also handles CSRF protection using Fetch Metadata for first-login scenarios.
|
|
42
|
+
*/
|
|
43
|
+
const originCheckMiddleware = createAuthMiddleware(async (ctx) => {
|
|
44
|
+
if (ctx.request?.method === "GET" || ctx.request?.method === "OPTIONS" || ctx.request?.method === "HEAD" || !ctx.request) return;
|
|
45
|
+
await validateOrigin(ctx);
|
|
46
|
+
if (shouldSkipOriginCheck(ctx)) return;
|
|
47
|
+
const { body, query } = ctx;
|
|
48
|
+
const callbackURL = body?.callbackURL || query?.callbackURL;
|
|
49
|
+
const redirectURL = body?.redirectTo;
|
|
50
|
+
const errorCallbackURL = body?.errorCallbackURL;
|
|
51
|
+
const newUserCallbackURL = body?.newUserCallbackURL;
|
|
52
|
+
const validateURL = (url, label) => {
|
|
53
|
+
if (!url) return;
|
|
54
|
+
if (typeof url !== "string") throw APIError.fromStatus("BAD_REQUEST", { message: `Invalid ${label}: expected a string` });
|
|
55
|
+
if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) {
|
|
56
|
+
ctx.context.logger.error(`Invalid ${label}: ${url}`);
|
|
57
|
+
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}`);
|
|
58
|
+
if (label === "origin") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_ORIGIN);
|
|
59
|
+
if (label === "callbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_CALLBACK_URL);
|
|
60
|
+
if (label === "redirectURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_REDIRECT_URL);
|
|
61
|
+
if (label === "errorCallbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL);
|
|
62
|
+
if (label === "newUserCallbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL);
|
|
63
|
+
throw APIError.fromStatus("FORBIDDEN", { message: `Invalid ${label}` });
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
callbackURL && validateURL(callbackURL, "callbackURL");
|
|
67
|
+
redirectURL && validateURL(redirectURL, "redirectURL");
|
|
68
|
+
errorCallbackURL && validateURL(errorCallbackURL, "errorCallbackURL");
|
|
69
|
+
newUserCallbackURL && validateURL(newUserCallbackURL, "newUserCallbackURL");
|
|
70
|
+
});
|
|
71
|
+
const originCheck = (getValue) => createAuthMiddleware(async (ctx) => {
|
|
72
|
+
if (!ctx.request) return;
|
|
73
|
+
if (shouldSkipOriginCheck(ctx)) return;
|
|
74
|
+
const callbackURL = getValue(ctx);
|
|
75
|
+
const validateURL = (url, label) => {
|
|
76
|
+
if (!url) return;
|
|
77
|
+
if (!ctx.context.isTrustedOrigin(url, { allowRelativePaths: label !== "origin" })) {
|
|
78
|
+
ctx.context.logger.error(`Invalid ${label}: ${url}`);
|
|
79
|
+
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}`);
|
|
80
|
+
if (label === "origin") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_ORIGIN);
|
|
81
|
+
if (label === "callbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_CALLBACK_URL);
|
|
82
|
+
if (label === "redirectURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_REDIRECT_URL);
|
|
83
|
+
if (label === "errorCallbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_ERROR_CALLBACK_URL);
|
|
84
|
+
if (label === "newUserCallbackURL") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_NEW_USER_CALLBACK_URL);
|
|
85
|
+
throw APIError.fromStatus("FORBIDDEN", { message: `Invalid ${label}` });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const callbacks = Array.isArray(callbackURL) ? callbackURL : [callbackURL];
|
|
89
|
+
for (const url of callbacks) validateURL(url, "callbackURL");
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* Validates origin header against trusted origins.
|
|
93
|
+
* @param ctx - The endpoint context
|
|
94
|
+
* @param forceValidate - If true, always validate origin regardless of cookies/skip flags
|
|
95
|
+
*/
|
|
96
|
+
async function validateOrigin(ctx, forceValidate = false) {
|
|
97
|
+
const headers = ctx.request?.headers;
|
|
98
|
+
if (!headers || !ctx.request) return;
|
|
99
|
+
const origin = headers.get("origin");
|
|
100
|
+
const originHeader = origin || headers.get("referer") || "";
|
|
101
|
+
const useCookies = headers.has("cookie");
|
|
102
|
+
if (ctx.context.skipCSRFCheck) return;
|
|
103
|
+
if (shouldSkipCSRFForBackwardCompat(ctx)) {
|
|
104
|
+
ctx.context.options.advanced?.disableOriginCheck === true && logBackwardCompatWarning();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (shouldSkipOriginCheck(ctx)) return;
|
|
108
|
+
if (!(forceValidate || useCookies)) return;
|
|
109
|
+
const inferredBaseURL = origin === "null" && headers.get("sec-fetch-site") === "same-origin" ? getBaseURL(void 0, ctx.context.options.basePath, ctx.request, false, ctx.context.options.advanced?.trustedProxyHeaders) : void 0;
|
|
110
|
+
const originToValidate = (inferredBaseURL ? getOrigin(inferredBaseURL) : void 0) ?? originHeader;
|
|
111
|
+
if (!originToValidate || originToValidate === "null") throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.MISSING_OR_NULL_ORIGIN);
|
|
112
|
+
const trustedOrigins = Array.isArray(ctx.context.options.trustedOrigins) ? ctx.context.trustedOrigins : [...ctx.context.trustedOrigins, ...(await ctx.context.options.trustedOrigins?.(ctx.request))?.filter((v) => Boolean(v)) || []];
|
|
113
|
+
if (!trustedOrigins.some((origin) => matchesOriginPattern(originToValidate, origin))) {
|
|
114
|
+
ctx.context.logger.error(`Invalid origin: ${originToValidate}`);
|
|
115
|
+
ctx.context.logger.info(`If it's a valid URL, please add ${originToValidate} to trustedOrigins in your auth config\n`, `Current list of trustedOrigins: ${trustedOrigins}`);
|
|
116
|
+
throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.INVALID_ORIGIN);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Middleware for CSRF protection using Fetch Metadata headers.
|
|
121
|
+
* This prevents cross-site navigation login attacks while supporting progressive enhancement.
|
|
122
|
+
*/
|
|
123
|
+
const formCsrfMiddleware = createAuthMiddleware(async (ctx) => {
|
|
124
|
+
if (!ctx.request) return;
|
|
125
|
+
await validateFormCsrf(ctx);
|
|
126
|
+
});
|
|
127
|
+
/**
|
|
128
|
+
* Validates CSRF protection for first-login scenarios using Fetch Metadata headers.
|
|
129
|
+
* This prevents cross-site form submission attacks while supporting progressive enhancement.
|
|
130
|
+
*/
|
|
131
|
+
async function validateFormCsrf(ctx) {
|
|
132
|
+
const req = ctx.request;
|
|
133
|
+
if (!req) return;
|
|
134
|
+
if (ctx.context.skipCSRFCheck) return;
|
|
135
|
+
if (shouldSkipCSRFForBackwardCompat(ctx)) return;
|
|
136
|
+
const headers = req.headers;
|
|
137
|
+
if (headers.has("cookie")) return await validateOrigin(ctx);
|
|
138
|
+
const site = headers.get("Sec-Fetch-Site");
|
|
139
|
+
const mode = headers.get("Sec-Fetch-Mode");
|
|
140
|
+
const dest = headers.get("Sec-Fetch-Dest");
|
|
141
|
+
if (Boolean(site && site.trim() || mode && mode.trim() || dest && dest.trim())) {
|
|
142
|
+
if (site === "cross-site" && mode === "navigate") {
|
|
143
|
+
ctx.context.logger.error("Blocked cross-site navigation login attempt (CSRF protection)", {
|
|
144
|
+
secFetchSite: site,
|
|
145
|
+
secFetchMode: mode,
|
|
146
|
+
secFetchDest: dest
|
|
147
|
+
});
|
|
148
|
+
throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.CROSS_SITE_NAVIGATION_LOGIN_BLOCKED);
|
|
149
|
+
}
|
|
150
|
+
return await validateOrigin(ctx, true);
|
|
151
|
+
}
|
|
152
|
+
if (headers.get("origin") || headers.get("referer")) return await validateOrigin(ctx, true);
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
export { formCsrfMiddleware, originCheck, originCheckMiddleware };
|