@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,230 @@
|
|
|
1
|
+
import { symmetricDecrypt } from "../../../crypto/index.mjs";
|
|
2
|
+
import { setSessionCookie } from "../../../cookies/index.mjs";
|
|
3
|
+
import { sessionMiddleware } from "../../../api/routes/session.mjs";
|
|
4
|
+
import { shouldRequirePassword } from "../../../utils/password.mjs";
|
|
5
|
+
import { PACKAGE_VERSION } from "../../../version.mjs";
|
|
6
|
+
import { TWO_FACTOR_ERROR_CODES } from "../error-code.mjs";
|
|
7
|
+
import { assertTwoFactorNotLocked, recordTwoFactorFailure, resetTwoFactorFailures, verifyTwoFactor } from "../verify-two-factor.mjs";
|
|
8
|
+
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
9
|
+
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
10
|
+
import * as z from "zod";
|
|
11
|
+
import { createOTP } from "@better-auth/utils/otp";
|
|
12
|
+
//#region src/plugins/two-factor/totp/index.ts
|
|
13
|
+
const generateTOTPBodySchema = z.object({ secret: z.string().meta({ description: "The secret to generate the TOTP code" }) });
|
|
14
|
+
const verifyTOTPBodySchema = z.object({
|
|
15
|
+
code: z.string().meta({ description: "The otp code to verify. Eg: \"012345\"" }),
|
|
16
|
+
/**
|
|
17
|
+
* if true, the device will be trusted
|
|
18
|
+
* for 30 days. It'll be refreshed on
|
|
19
|
+
* every sign in request within this time.
|
|
20
|
+
*/
|
|
21
|
+
trustDevice: z.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional()
|
|
22
|
+
});
|
|
23
|
+
const totp2fa = (options) => {
|
|
24
|
+
const opts = {
|
|
25
|
+
...options,
|
|
26
|
+
digits: options?.digits || 6,
|
|
27
|
+
period: options?.period || 30
|
|
28
|
+
};
|
|
29
|
+
const passwordSchema = z.string().meta({ description: "User password" });
|
|
30
|
+
const getTOTPURIBodySchema = options?.allowPasswordless ? z.object({ password: passwordSchema.optional() }) : z.object({ password: passwordSchema });
|
|
31
|
+
const twoFactorTable = "twoFactor";
|
|
32
|
+
return {
|
|
33
|
+
id: "totp",
|
|
34
|
+
version: PACKAGE_VERSION,
|
|
35
|
+
endpoints: {
|
|
36
|
+
/**
|
|
37
|
+
* ### Endpoint
|
|
38
|
+
*
|
|
39
|
+
* POST `/totp/generate`
|
|
40
|
+
*
|
|
41
|
+
* ### API Methods
|
|
42
|
+
*
|
|
43
|
+
* **server:**
|
|
44
|
+
* `auth.api.generateTOTP`
|
|
45
|
+
*
|
|
46
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)
|
|
47
|
+
*/
|
|
48
|
+
generateTOTP: createAuthEndpoint.serverOnly({
|
|
49
|
+
method: "POST",
|
|
50
|
+
body: generateTOTPBodySchema,
|
|
51
|
+
metadata: { openapi: {
|
|
52
|
+
summary: "Generate TOTP code",
|
|
53
|
+
description: "Use this endpoint to generate a TOTP code",
|
|
54
|
+
responses: { 200: {
|
|
55
|
+
description: "Successful response",
|
|
56
|
+
content: { "application/json": { schema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: { code: { type: "string" } }
|
|
59
|
+
} } }
|
|
60
|
+
} }
|
|
61
|
+
} }
|
|
62
|
+
}, async (ctx) => {
|
|
63
|
+
if (options?.disable) {
|
|
64
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
65
|
+
throw APIError.from("BAD_REQUEST", {
|
|
66
|
+
message: "totp isn't configured",
|
|
67
|
+
code: "TOTP_NOT_CONFIGURED"
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return { code: await createOTP(ctx.body.secret, {
|
|
71
|
+
period: opts.period,
|
|
72
|
+
digits: opts.digits
|
|
73
|
+
}).totp() };
|
|
74
|
+
}),
|
|
75
|
+
/**
|
|
76
|
+
* ### Endpoint
|
|
77
|
+
*
|
|
78
|
+
* POST `/two-factor/get-totp-uri`
|
|
79
|
+
*
|
|
80
|
+
* ### API Methods
|
|
81
|
+
*
|
|
82
|
+
* **server:**
|
|
83
|
+
* `auth.api.getTOTPURI`
|
|
84
|
+
*
|
|
85
|
+
* **client:**
|
|
86
|
+
* `authClient.twoFactor.getTotpUri`
|
|
87
|
+
*
|
|
88
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)
|
|
89
|
+
*/
|
|
90
|
+
getTOTPURI: createAuthEndpoint("/two-factor/get-totp-uri", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
use: [sessionMiddleware],
|
|
93
|
+
body: getTOTPURIBodySchema,
|
|
94
|
+
metadata: { openapi: {
|
|
95
|
+
summary: "Get TOTP URI",
|
|
96
|
+
description: "Use this endpoint to get the TOTP URI",
|
|
97
|
+
responses: { 200: {
|
|
98
|
+
description: "Successful response",
|
|
99
|
+
content: { "application/json": { schema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: { totpURI: { type: "string" } }
|
|
102
|
+
} } }
|
|
103
|
+
} }
|
|
104
|
+
} }
|
|
105
|
+
}, async (ctx) => {
|
|
106
|
+
if (options?.disable) {
|
|
107
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
108
|
+
throw APIError.from("BAD_REQUEST", {
|
|
109
|
+
message: "totp isn't configured",
|
|
110
|
+
code: "TOTP_NOT_CONFIGURED"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const user = ctx.context.session.user;
|
|
114
|
+
const twoFactor = await ctx.context.adapter.findOne({
|
|
115
|
+
model: twoFactorTable,
|
|
116
|
+
where: [{
|
|
117
|
+
field: "userId",
|
|
118
|
+
value: user.id
|
|
119
|
+
}]
|
|
120
|
+
});
|
|
121
|
+
if (!twoFactor) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED);
|
|
122
|
+
const secret = await symmetricDecrypt({
|
|
123
|
+
key: ctx.context.secretConfig,
|
|
124
|
+
data: twoFactor.secret
|
|
125
|
+
});
|
|
126
|
+
if (await shouldRequirePassword(ctx, user.id, options?.allowPasswordless)) {
|
|
127
|
+
if (!ctx.body.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
|
|
128
|
+
await ctx.context.password.checkPassword(user.id, ctx);
|
|
129
|
+
}
|
|
130
|
+
return { totpURI: createOTP(secret, {
|
|
131
|
+
digits: opts.digits,
|
|
132
|
+
period: opts.period
|
|
133
|
+
}).url(options?.issuer || ctx.context.appName, user.email) };
|
|
134
|
+
}),
|
|
135
|
+
/**
|
|
136
|
+
* ### Endpoint
|
|
137
|
+
*
|
|
138
|
+
* POST `/two-factor/verify-totp`
|
|
139
|
+
*
|
|
140
|
+
* ### API Methods
|
|
141
|
+
*
|
|
142
|
+
* **server:**
|
|
143
|
+
* `auth.api.verifyTOTP`
|
|
144
|
+
*
|
|
145
|
+
* **client:**
|
|
146
|
+
* `authClient.twoFactor.verifyTotp`
|
|
147
|
+
*
|
|
148
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)
|
|
149
|
+
*/
|
|
150
|
+
verifyTOTP: createAuthEndpoint("/two-factor/verify-totp", {
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: verifyTOTPBodySchema,
|
|
153
|
+
metadata: { openapi: {
|
|
154
|
+
summary: "Verify two factor TOTP",
|
|
155
|
+
description: "Verify two factor TOTP",
|
|
156
|
+
responses: { 200: {
|
|
157
|
+
description: "Successful response",
|
|
158
|
+
content: { "application/json": { schema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: { status: { type: "boolean" } }
|
|
161
|
+
} } }
|
|
162
|
+
} }
|
|
163
|
+
} }
|
|
164
|
+
}, async (ctx) => {
|
|
165
|
+
if (options?.disable) {
|
|
166
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
167
|
+
throw APIError.from("BAD_REQUEST", {
|
|
168
|
+
message: "totp isn't configured",
|
|
169
|
+
code: "TOTP_NOT_CONFIGURED"
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const { session, valid, invalid, beginAttempt } = await verifyTwoFactor(ctx);
|
|
173
|
+
const user = session.user;
|
|
174
|
+
const isSignIn = !session.session;
|
|
175
|
+
const twoFactor = await ctx.context.adapter.findOne({
|
|
176
|
+
model: twoFactorTable,
|
|
177
|
+
where: [{
|
|
178
|
+
field: "userId",
|
|
179
|
+
value: user.id
|
|
180
|
+
}]
|
|
181
|
+
});
|
|
182
|
+
if (!twoFactor) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED);
|
|
183
|
+
if (isSignIn && twoFactor.verified === false) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED);
|
|
184
|
+
if (isSignIn) await assertTwoFactorNotLocked(ctx, twoFactorTable, twoFactor);
|
|
185
|
+
const attempt = isSignIn ? await beginAttempt(5) : null;
|
|
186
|
+
let status;
|
|
187
|
+
try {
|
|
188
|
+
status = await createOTP(await symmetricDecrypt({
|
|
189
|
+
key: ctx.context.secretConfig,
|
|
190
|
+
data: twoFactor.secret
|
|
191
|
+
}), {
|
|
192
|
+
period: opts.period,
|
|
193
|
+
digits: opts.digits
|
|
194
|
+
}).verify(ctx.body.code);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
await attempt?.restore();
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
if (!status) {
|
|
200
|
+
await attempt?.recordFailure();
|
|
201
|
+
if (isSignIn) await recordTwoFactorFailure(ctx, twoFactorTable, twoFactor);
|
|
202
|
+
return invalid("INVALID_CODE");
|
|
203
|
+
}
|
|
204
|
+
if (isSignIn) await resetTwoFactorFailures(ctx, twoFactorTable, twoFactor);
|
|
205
|
+
if (twoFactor.verified !== true) {
|
|
206
|
+
if (!user.twoFactorEnabled) {
|
|
207
|
+
const activeSession = session.session;
|
|
208
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: true });
|
|
209
|
+
await setSessionCookie(ctx, {
|
|
210
|
+
session: await ctx.context.internalAdapter.createSession(user.id, false, activeSession),
|
|
211
|
+
user: updatedUser
|
|
212
|
+
});
|
|
213
|
+
await ctx.context.internalAdapter.deleteSession(activeSession.token);
|
|
214
|
+
}
|
|
215
|
+
await ctx.context.adapter.update({
|
|
216
|
+
model: twoFactorTable,
|
|
217
|
+
update: { verified: true },
|
|
218
|
+
where: [{
|
|
219
|
+
field: "id",
|
|
220
|
+
value: twoFactor.id
|
|
221
|
+
}]
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return valid(ctx);
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
//#endregion
|
|
230
|
+
export { totp2fa };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { User } from "../../types/models.mjs";
|
|
2
|
+
import { InferOptionSchema } from "../../types/plugins.mjs";
|
|
3
|
+
import { BackupCodeOptions } from "./backup-codes/index.mjs";
|
|
4
|
+
import { OTPOptions } from "./otp/index.mjs";
|
|
5
|
+
import { schema } from "./schema.mjs";
|
|
6
|
+
import { TOTPOptions } from "./totp/index.mjs";
|
|
7
|
+
import { BetterAuthPlugin, LiteralString } from "@better-auth/core";
|
|
8
|
+
|
|
9
|
+
//#region src/plugins/two-factor/types.d.ts
|
|
10
|
+
interface TwoFactorOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Application Name
|
|
13
|
+
*/
|
|
14
|
+
issuer?: string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The name of the table that stores the two factor
|
|
17
|
+
* authentication data.
|
|
18
|
+
*
|
|
19
|
+
* @default "twoFactor"
|
|
20
|
+
*/
|
|
21
|
+
twoFactorTable?: string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* TOTP OPtions
|
|
24
|
+
*/
|
|
25
|
+
totpOptions?: Omit<TOTPOptions, "issuer"> | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* OTP Options
|
|
28
|
+
*/
|
|
29
|
+
otpOptions?: OTPOptions | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Backup code options
|
|
32
|
+
*/
|
|
33
|
+
backupCodeOptions?: BackupCodeOptions | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Skip verification on enabling two factor authentication.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
skipVerificationOnEnable?: boolean | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Allow enabling and managing 2FA without a password when the user does not
|
|
41
|
+
* have a credential account (e.g. passkey-only users).
|
|
42
|
+
* When enabled, password is still required if a credential account exists.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
allowPasswordless?: boolean | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Custom schema for the two factor plugin
|
|
48
|
+
*/
|
|
49
|
+
schema?: InferOptionSchema<typeof schema> | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Maximum age (in seconds) for the two-factor verification cookie.
|
|
52
|
+
* This controls how long users have to complete the 2FA flow
|
|
53
|
+
* after signing in.
|
|
54
|
+
*
|
|
55
|
+
* @default 600 (10 minutes)
|
|
56
|
+
*/
|
|
57
|
+
twoFactorCookieMaxAge?: number | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Maximum age (in seconds) for the trusted device cookie.
|
|
60
|
+
* When a user opts to trust a device, this controls how long
|
|
61
|
+
* the device stays trusted before requiring 2FA again.
|
|
62
|
+
*
|
|
63
|
+
* @default 2592000 (30 days)
|
|
64
|
+
*/
|
|
65
|
+
trustDeviceMaxAge?: number | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Account-level lockout for failed second-factor verifications during
|
|
68
|
+
* sign-in. Caps consecutive failed attempts per account, across challenges
|
|
69
|
+
* and factors, and resets on a successful verification.
|
|
70
|
+
*/
|
|
71
|
+
accountLockout?: {
|
|
72
|
+
/**
|
|
73
|
+
* Whether account-level lockout is enforced.
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
enabled?: boolean | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Consecutive failed verifications, across challenges and factors,
|
|
79
|
+
* before the account is locked.
|
|
80
|
+
* @default 10
|
|
81
|
+
*/
|
|
82
|
+
maxFailedAttempts?: number | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* How long the account stays locked, in seconds.
|
|
85
|
+
* @default 900 (15 minutes)
|
|
86
|
+
*/
|
|
87
|
+
durationSeconds?: number | undefined;
|
|
88
|
+
} | undefined;
|
|
89
|
+
}
|
|
90
|
+
interface UserWithTwoFactor extends User {
|
|
91
|
+
/**
|
|
92
|
+
* If the user has enabled two factor authentication.
|
|
93
|
+
*/
|
|
94
|
+
twoFactorEnabled: boolean;
|
|
95
|
+
}
|
|
96
|
+
interface TwoFactorProvider {
|
|
97
|
+
id: LiteralString;
|
|
98
|
+
version?: string | undefined;
|
|
99
|
+
endpoints?: BetterAuthPlugin["endpoints"] | undefined;
|
|
100
|
+
}
|
|
101
|
+
interface TwoFactorTable {
|
|
102
|
+
id: string;
|
|
103
|
+
userId: string;
|
|
104
|
+
secret: string;
|
|
105
|
+
backupCodes: string;
|
|
106
|
+
verified: boolean;
|
|
107
|
+
failedVerificationCount?: number;
|
|
108
|
+
lockedUntil?: Date | null;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { base64Url } from "@better-auth/utils/base64";
|
|
2
|
+
import { createHash } from "@better-auth/utils/hash";
|
|
3
|
+
//#region src/plugins/two-factor/utils.ts
|
|
4
|
+
const defaultKeyHasher = async (token) => {
|
|
5
|
+
const hash = await createHash("SHA-256").digest(new TextEncoder().encode(token));
|
|
6
|
+
return base64Url.encode(new Uint8Array(hash), { padding: false });
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { defaultKeyHasher };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { parseUserOutput } from "../../db/schema.mjs";
|
|
2
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
|
+
import { expireCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
4
|
+
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
5
|
+
import { TWO_FACTOR_ERROR_CODES } from "./error-code.mjs";
|
|
6
|
+
import { TRUST_DEVICE_COOKIE_NAME, TWO_FACTOR_COOKIE_NAME } from "./constant.mjs";
|
|
7
|
+
import { APIError } from "@better-auth/core/error";
|
|
8
|
+
import { createHMAC } from "@better-auth/utils/hmac";
|
|
9
|
+
//#region src/plugins/two-factor/verify-two-factor.ts
|
|
10
|
+
async function verifyTwoFactor(ctx) {
|
|
11
|
+
const invalid = (errorKey) => {
|
|
12
|
+
throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES[errorKey]);
|
|
13
|
+
};
|
|
14
|
+
const session = await getSessionFromCtx(ctx);
|
|
15
|
+
if (!session) {
|
|
16
|
+
const twoFactorCookie = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
|
|
17
|
+
const signedTwoFactorCookie = await ctx.getSignedCookie(twoFactorCookie.name, ctx.context.secret);
|
|
18
|
+
if (!signedTwoFactorCookie) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
19
|
+
const verificationToken = await ctx.context.internalAdapter.findVerificationValue(signedTwoFactorCookie);
|
|
20
|
+
if (!verificationToken) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
21
|
+
const user = await ctx.context.internalAdapter.findUserById(verificationToken.value);
|
|
22
|
+
if (!user) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
23
|
+
const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret);
|
|
24
|
+
return {
|
|
25
|
+
valid: async (ctx) => {
|
|
26
|
+
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(signedTwoFactorCookie);
|
|
27
|
+
if (!consumed || consumed.value !== user.id) {
|
|
28
|
+
expireCookie(ctx, twoFactorCookie);
|
|
29
|
+
throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
30
|
+
}
|
|
31
|
+
const session = await ctx.context.internalAdapter.createSession(consumed.value, !!dontRememberMe);
|
|
32
|
+
if (!session) throw APIError.from("INTERNAL_SERVER_ERROR", {
|
|
33
|
+
message: "failed to create session",
|
|
34
|
+
code: "FAILED_TO_CREATE_SESSION"
|
|
35
|
+
});
|
|
36
|
+
await setSessionCookie(ctx, {
|
|
37
|
+
session,
|
|
38
|
+
user
|
|
39
|
+
});
|
|
40
|
+
expireCookie(ctx, twoFactorCookie);
|
|
41
|
+
if (ctx.body.trustDevice) {
|
|
42
|
+
const maxAge = ctx.context.getPlugin("two-factor").options?.trustDeviceMaxAge ?? 2592e3;
|
|
43
|
+
const trustDeviceCookie = ctx.context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME, { maxAge });
|
|
44
|
+
/**
|
|
45
|
+
* Create a random identifier for the trust device record.
|
|
46
|
+
* Store it in the verification table with an expiration
|
|
47
|
+
* so the server can validate and revoke it.
|
|
48
|
+
*/
|
|
49
|
+
const trustIdentifier = `trust-device-${generateRandomString(32)}`;
|
|
50
|
+
const token = await createHMAC("SHA-256", "base64urlnopad").sign(ctx.context.secret, `${user.id}!${trustIdentifier}`);
|
|
51
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
52
|
+
value: user.id,
|
|
53
|
+
identifier: trustIdentifier,
|
|
54
|
+
expiresAt: new Date(Date.now() + maxAge * 1e3)
|
|
55
|
+
});
|
|
56
|
+
await ctx.setSignedCookie(trustDeviceCookie.name, `${token}!${trustIdentifier}`, ctx.context.secret, trustDeviceCookie.attributes);
|
|
57
|
+
expireCookie(ctx, ctx.context.authCookies.dontRememberToken);
|
|
58
|
+
}
|
|
59
|
+
return ctx.json({
|
|
60
|
+
token: session.token,
|
|
61
|
+
user: parseUserOutput(ctx.context.options, user)
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
invalid,
|
|
65
|
+
session: {
|
|
66
|
+
session: null,
|
|
67
|
+
user
|
|
68
|
+
},
|
|
69
|
+
key: signedTwoFactorCookie,
|
|
70
|
+
beginAttempt: async (allowedAttempts) => {
|
|
71
|
+
const identifier = `2fa-attempts-${signedTwoFactorCookie}`;
|
|
72
|
+
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(identifier).catch(() => null);
|
|
73
|
+
if (!consumed) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
|
|
74
|
+
const parsed = Number(consumed.value);
|
|
75
|
+
const attempts = Number.isInteger(parsed) && parsed >= 0 ? parsed : allowedAttempts;
|
|
76
|
+
if (attempts >= allowedAttempts) {
|
|
77
|
+
try {
|
|
78
|
+
await ctx.context.internalAdapter.consumeVerificationValue(signedTwoFactorCookie);
|
|
79
|
+
} catch {
|
|
80
|
+
expireCookie(ctx, twoFactorCookie);
|
|
81
|
+
throw APIError.from("INTERNAL_SERVER_ERROR", {
|
|
82
|
+
message: "Failed to invalidate two-factor challenge",
|
|
83
|
+
code: "FAILED_TO_INVALIDATE_TWO_FACTOR_CHALLENGE"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
expireCookie(ctx, twoFactorCookie);
|
|
87
|
+
throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE);
|
|
88
|
+
}
|
|
89
|
+
const rearm = (count) => ctx.context.internalAdapter.createVerificationValue({
|
|
90
|
+
value: `${count}`,
|
|
91
|
+
identifier,
|
|
92
|
+
expiresAt: verificationToken.expiresAt
|
|
93
|
+
}).catch(() => {});
|
|
94
|
+
return {
|
|
95
|
+
recordFailure: () => rearm(attempts + 1),
|
|
96
|
+
restore: () => rearm(attempts)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
valid: async (ctx) => {
|
|
103
|
+
return ctx.json({
|
|
104
|
+
token: session.session.token,
|
|
105
|
+
user: parseUserOutput(ctx.context.options, session.user)
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
invalid,
|
|
109
|
+
session,
|
|
110
|
+
key: `${session.user.id}!${session.session.id}`,
|
|
111
|
+
beginAttempt: async (_allowedAttempts) => ({
|
|
112
|
+
recordFailure: async () => {},
|
|
113
|
+
restore: async () => {}
|
|
114
|
+
})
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function resolveAccountLockoutConfig(ctx) {
|
|
118
|
+
const lockout = (ctx.context.getPlugin("two-factor")?.options)?.accountLockout;
|
|
119
|
+
return {
|
|
120
|
+
enabled: lockout?.enabled ?? true,
|
|
121
|
+
maxFailedAttempts: lockout?.maxFailedAttempts ?? 10,
|
|
122
|
+
durationMs: (lockout?.durationSeconds ?? 900) * 1e3
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Reject the verification when the account is locked, and lazily clear an
|
|
127
|
+
* expired lock. The lock caps consecutive failed verifications per account,
|
|
128
|
+
* across challenges and factors (NIST SP 800-63B §5.2.2).
|
|
129
|
+
*/
|
|
130
|
+
async function assertTwoFactorNotLocked(ctx, twoFactorTable, twoFactor) {
|
|
131
|
+
const { enabled } = resolveAccountLockoutConfig(ctx);
|
|
132
|
+
if (!enabled || !twoFactor.lockedUntil) return;
|
|
133
|
+
if (new Date(twoFactor.lockedUntil).getTime() > Date.now()) throw APIError.from("TOO_MANY_REQUESTS", TWO_FACTOR_ERROR_CODES.ACCOUNT_TEMPORARILY_LOCKED);
|
|
134
|
+
await ctx.context.adapter.incrementOne({
|
|
135
|
+
model: twoFactorTable,
|
|
136
|
+
where: [{
|
|
137
|
+
field: "id",
|
|
138
|
+
value: twoFactor.id
|
|
139
|
+
}, {
|
|
140
|
+
field: "lockedUntil",
|
|
141
|
+
operator: "lte",
|
|
142
|
+
value: /* @__PURE__ */ new Date()
|
|
143
|
+
}],
|
|
144
|
+
increment: {},
|
|
145
|
+
set: {
|
|
146
|
+
failedVerificationCount: 0,
|
|
147
|
+
lockedUntil: null
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Count one failed verification toward the account-level budget, and lock the
|
|
153
|
+
* account once the budget is spent. The increment is atomic, so concurrent
|
|
154
|
+
* failures cannot lose updates. It is unguarded so it still applies to a row
|
|
155
|
+
* whose counter is null or absent (a row created before the migration, or a
|
|
156
|
+
* document-store record predating the column), where a guarded comparison
|
|
157
|
+
* would never match.
|
|
158
|
+
*/
|
|
159
|
+
async function recordTwoFactorFailure(ctx, twoFactorTable, twoFactor) {
|
|
160
|
+
const { enabled, maxFailedAttempts, durationMs } = resolveAccountLockoutConfig(ctx);
|
|
161
|
+
if (!enabled) return;
|
|
162
|
+
if (((await ctx.context.adapter.incrementOne({
|
|
163
|
+
model: twoFactorTable,
|
|
164
|
+
where: [{
|
|
165
|
+
field: "id",
|
|
166
|
+
value: twoFactor.id
|
|
167
|
+
}],
|
|
168
|
+
increment: { failedVerificationCount: 1 }
|
|
169
|
+
}))?.failedVerificationCount ?? 0) >= maxFailedAttempts) await ctx.context.adapter.incrementOne({
|
|
170
|
+
model: twoFactorTable,
|
|
171
|
+
where: [{
|
|
172
|
+
field: "id",
|
|
173
|
+
value: twoFactor.id
|
|
174
|
+
}, {
|
|
175
|
+
field: "failedVerificationCount",
|
|
176
|
+
operator: "gte",
|
|
177
|
+
value: maxFailedAttempts
|
|
178
|
+
}],
|
|
179
|
+
increment: {},
|
|
180
|
+
set: { lockedUntil: new Date(Date.now() + durationMs) }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clear the account-level failure budget after a successful verification, so the
|
|
185
|
+
* count tracks only consecutive failures. The write is unconditional: a snapshot
|
|
186
|
+
* read at the start of the request can miss a concurrent failure, so skipping it
|
|
187
|
+
* could leave the counter non-zero after a success.
|
|
188
|
+
*/
|
|
189
|
+
async function resetTwoFactorFailures(ctx, twoFactorTable, twoFactor) {
|
|
190
|
+
const { enabled } = resolveAccountLockoutConfig(ctx);
|
|
191
|
+
if (!enabled) return;
|
|
192
|
+
await ctx.context.adapter.update({
|
|
193
|
+
model: twoFactorTable,
|
|
194
|
+
where: [{
|
|
195
|
+
field: "id",
|
|
196
|
+
value: twoFactor.id
|
|
197
|
+
}],
|
|
198
|
+
update: {
|
|
199
|
+
failedVerificationCount: 0,
|
|
200
|
+
lockedUntil: null
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { assertTwoFactorNotLocked, recordTwoFactorFailure, resetTwoFactorFailures, verifyTwoFactor };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
2
|
+
import { UsernamePlugin, UsernamePluginWithoutDisplayUsername } from "./index.mjs";
|
|
3
|
+
import * as _$_better_auth_core_utils_error_codes0 from "@better-auth/core/utils/error-codes";
|
|
4
|
+
|
|
5
|
+
//#region src/plugins/username/client.d.ts
|
|
6
|
+
type UsernameClientOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the server has the `displayUsername` field enabled.
|
|
9
|
+
*
|
|
10
|
+
* This must mirror the `displayUsername` option passed to the server
|
|
11
|
+
* `username` plugin so the client infers the correct user/session types.
|
|
12
|
+
*
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
displayUsername?: boolean | undefined;
|
|
16
|
+
};
|
|
17
|
+
declare const usernameClient: <O extends UsernameClientOptions = UsernameClientOptions>(options?: O) => {
|
|
18
|
+
id: "username";
|
|
19
|
+
version: string;
|
|
20
|
+
$InferServerPlugin: O["displayUsername"] extends false ? UsernamePluginWithoutDisplayUsername : UsernamePlugin;
|
|
21
|
+
atomListeners: {
|
|
22
|
+
matcher: (path: string) => path is "/sign-in/username";
|
|
23
|
+
signal: "$sessionSignal";
|
|
24
|
+
}[];
|
|
25
|
+
$ERROR_CODES: {
|
|
26
|
+
EMAIL_NOT_VERIFIED: _$_better_auth_core_utils_error_codes0.RawError<"EMAIL_NOT_VERIFIED">;
|
|
27
|
+
UNEXPECTED_ERROR: _$_better_auth_core_utils_error_codes0.RawError<"UNEXPECTED_ERROR">;
|
|
28
|
+
INVALID_USERNAME_OR_PASSWORD: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_USERNAME_OR_PASSWORD">;
|
|
29
|
+
USERNAME_IS_ALREADY_TAKEN: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_IS_ALREADY_TAKEN">;
|
|
30
|
+
USERNAME_TOO_SHORT: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_TOO_SHORT">;
|
|
31
|
+
USERNAME_TOO_LONG: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_TOO_LONG">;
|
|
32
|
+
INVALID_USERNAME: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_USERNAME">;
|
|
33
|
+
INVALID_DISPLAY_USERNAME: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_DISPLAY_USERNAME">;
|
|
34
|
+
USERNAME_IS_IMMUTABLE: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_IS_IMMUTABLE">;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { UsernameClientOptions, usernameClient };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PACKAGE_VERSION } from "../../version.mjs";
|
|
2
|
+
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
3
|
+
//#region src/plugins/username/client.ts
|
|
4
|
+
const usernameClient = (options) => {
|
|
5
|
+
return {
|
|
6
|
+
id: "username",
|
|
7
|
+
version: PACKAGE_VERSION,
|
|
8
|
+
$InferServerPlugin: {},
|
|
9
|
+
atomListeners: [{
|
|
10
|
+
matcher: (path) => path === "/sign-in/username",
|
|
11
|
+
signal: "$sessionSignal"
|
|
12
|
+
}],
|
|
13
|
+
$ERROR_CODES: USERNAME_ERROR_CODES
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { usernameClient };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as _$_better_auth_core_utils_error_codes0 from "@better-auth/core/utils/error-codes";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/username/error-codes.d.ts
|
|
4
|
+
declare const USERNAME_ERROR_CODES: {
|
|
5
|
+
EMAIL_NOT_VERIFIED: _$_better_auth_core_utils_error_codes0.RawError<"EMAIL_NOT_VERIFIED">;
|
|
6
|
+
UNEXPECTED_ERROR: _$_better_auth_core_utils_error_codes0.RawError<"UNEXPECTED_ERROR">;
|
|
7
|
+
INVALID_USERNAME_OR_PASSWORD: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_USERNAME_OR_PASSWORD">;
|
|
8
|
+
USERNAME_IS_ALREADY_TAKEN: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_IS_ALREADY_TAKEN">;
|
|
9
|
+
USERNAME_TOO_SHORT: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_TOO_SHORT">;
|
|
10
|
+
USERNAME_TOO_LONG: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_TOO_LONG">;
|
|
11
|
+
INVALID_USERNAME: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_USERNAME">;
|
|
12
|
+
INVALID_DISPLAY_USERNAME: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_DISPLAY_USERNAME">;
|
|
13
|
+
USERNAME_IS_IMMUTABLE: _$_better_auth_core_utils_error_codes0.RawError<"USERNAME_IS_IMMUTABLE">;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { USERNAME_ERROR_CODES };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
|
|
2
|
+
//#region src/plugins/username/error-codes.ts
|
|
3
|
+
const USERNAME_ERROR_CODES = defineErrorCodes({
|
|
4
|
+
INVALID_USERNAME_OR_PASSWORD: "Invalid username or password",
|
|
5
|
+
EMAIL_NOT_VERIFIED: "Email not verified",
|
|
6
|
+
UNEXPECTED_ERROR: "Unexpected error",
|
|
7
|
+
USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.",
|
|
8
|
+
USERNAME_TOO_SHORT: "Username is too short",
|
|
9
|
+
USERNAME_TOO_LONG: "Username is too long",
|
|
10
|
+
INVALID_USERNAME: "Username is invalid",
|
|
11
|
+
INVALID_DISPLAY_USERNAME: "Display username is invalid",
|
|
12
|
+
USERNAME_IS_IMMUTABLE: "Username cannot be updated"
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { USERNAME_ERROR_CODES };
|