@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,529 @@
|
|
|
1
|
+
import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
|
|
2
|
+
import { getDate } from "../../utils/date.mjs";
|
|
3
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
|
+
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
5
|
+
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
6
|
+
import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
|
|
7
|
+
import { PHONE_NUMBER_ERROR_CODES } from "./error-codes.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
|
+
//#region src/plugins/phone-number/routes.ts
|
|
12
|
+
const signInPhoneNumberBodySchema = z.object({
|
|
13
|
+
phoneNumber: z.string().meta({ description: "Phone number to sign in. Eg: \"+1234567890\"" }),
|
|
14
|
+
password: z.string().meta({ description: "Password to use for sign in." }),
|
|
15
|
+
rememberMe: z.boolean().meta({ description: "Remember the session. Eg: true" }).optional()
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* ### Endpoint
|
|
19
|
+
*
|
|
20
|
+
* POST `/sign-in/phone-number`
|
|
21
|
+
*
|
|
22
|
+
* ### API Methods
|
|
23
|
+
*
|
|
24
|
+
* **server:**
|
|
25
|
+
* `auth.api.signInPhoneNumber`
|
|
26
|
+
*
|
|
27
|
+
* **client:**
|
|
28
|
+
* `authClient.signIn.phoneNumber`
|
|
29
|
+
*
|
|
30
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/phone-number#api-method-sign-in-phone-number)
|
|
31
|
+
*/
|
|
32
|
+
const signInPhoneNumber = (opts) => createAuthEndpoint("/sign-in/phone-number", {
|
|
33
|
+
method: "POST",
|
|
34
|
+
body: signInPhoneNumberBodySchema,
|
|
35
|
+
metadata: { openapi: {
|
|
36
|
+
summary: "Sign in with phone number",
|
|
37
|
+
description: "Use this endpoint to sign in with phone number",
|
|
38
|
+
responses: {
|
|
39
|
+
200: {
|
|
40
|
+
description: "Success",
|
|
41
|
+
content: { "application/json": { schema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
user: { $ref: "#/components/schemas/User" },
|
|
45
|
+
session: { $ref: "#/components/schemas/Session" }
|
|
46
|
+
}
|
|
47
|
+
} } }
|
|
48
|
+
},
|
|
49
|
+
400: { description: "Invalid phone number or password" }
|
|
50
|
+
}
|
|
51
|
+
} }
|
|
52
|
+
}, async (ctx) => {
|
|
53
|
+
const { password, phoneNumber } = ctx.body;
|
|
54
|
+
if (opts.phoneNumberValidator) {
|
|
55
|
+
if (!await opts.phoneNumberValidator(ctx.body.phoneNumber)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER);
|
|
56
|
+
}
|
|
57
|
+
const user = await ctx.context.adapter.findOne({
|
|
58
|
+
model: "user",
|
|
59
|
+
where: [{
|
|
60
|
+
field: "phoneNumber",
|
|
61
|
+
value: phoneNumber
|
|
62
|
+
}]
|
|
63
|
+
});
|
|
64
|
+
if (!user) throw APIError.from("UNAUTHORIZED", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER_OR_PASSWORD);
|
|
65
|
+
if (opts.requireVerification) {
|
|
66
|
+
if (!user.phoneNumberVerified) {
|
|
67
|
+
const otp = generateOTP(opts.otpLength);
|
|
68
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
69
|
+
value: otp,
|
|
70
|
+
identifier: phoneNumber,
|
|
71
|
+
expiresAt: getDate(opts.expiresIn, "sec")
|
|
72
|
+
});
|
|
73
|
+
if (opts.sendOTP) await ctx.context.runInBackgroundOrAwait(opts.sendOTP({
|
|
74
|
+
phoneNumber,
|
|
75
|
+
code: otp
|
|
76
|
+
}, ctx));
|
|
77
|
+
throw APIError.from("UNAUTHORIZED", PHONE_NUMBER_ERROR_CODES.PHONE_NUMBER_NOT_VERIFIED);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const credentialAccount = await ctx.context.internalAdapter.findCredentialAccount(user.id);
|
|
81
|
+
if (!credentialAccount) {
|
|
82
|
+
ctx.context.logger.warn("Credential account not found");
|
|
83
|
+
throw APIError.from("UNAUTHORIZED", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER_OR_PASSWORD);
|
|
84
|
+
}
|
|
85
|
+
const currentPassword = credentialAccount?.password;
|
|
86
|
+
if (!currentPassword) {
|
|
87
|
+
ctx.context.logger.warn("Password not found");
|
|
88
|
+
throw APIError.from("UNAUTHORIZED", PHONE_NUMBER_ERROR_CODES.UNEXPECTED_ERROR);
|
|
89
|
+
}
|
|
90
|
+
if (!await ctx.context.password.verify({
|
|
91
|
+
hash: currentPassword,
|
|
92
|
+
password
|
|
93
|
+
})) {
|
|
94
|
+
ctx.context.logger.warn("Invalid password");
|
|
95
|
+
throw APIError.from("UNAUTHORIZED", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER_OR_PASSWORD);
|
|
96
|
+
}
|
|
97
|
+
const session = await ctx.context.internalAdapter.createSession(user.id, ctx.body.rememberMe === false);
|
|
98
|
+
if (!session) {
|
|
99
|
+
ctx.context.logger.error("Failed to create session");
|
|
100
|
+
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
|
|
101
|
+
}
|
|
102
|
+
await setSessionCookie(ctx, {
|
|
103
|
+
session,
|
|
104
|
+
user
|
|
105
|
+
}, ctx.body.rememberMe === false);
|
|
106
|
+
return ctx.json({
|
|
107
|
+
token: session.token,
|
|
108
|
+
user: parseUserOutput(ctx.context.options, user)
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
const sendPhoneNumberOTPBodySchema = z.object({ phoneNumber: z.string().meta({ description: "Phone number to send OTP. Eg: \"+1234567890\"" }) });
|
|
112
|
+
const consumePhoneNumberOTPBodySchema = z.object({
|
|
113
|
+
phoneNumber: z.string().meta({ description: "Phone number to verify. Eg: \"+1234567890\"" }),
|
|
114
|
+
code: z.string().meta({ description: "OTP code. Eg: \"123456\"" })
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* ### Endpoint
|
|
118
|
+
*
|
|
119
|
+
* POST `/phone-number/send-otp`
|
|
120
|
+
*
|
|
121
|
+
* ### API Methods
|
|
122
|
+
*
|
|
123
|
+
* **server:**
|
|
124
|
+
* `auth.api.sendPhoneNumberOTP`
|
|
125
|
+
*
|
|
126
|
+
* **client:**
|
|
127
|
+
* `authClient.phoneNumber.sendOtp`
|
|
128
|
+
*
|
|
129
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/phone-number#api-method-phone-number-send-otp)
|
|
130
|
+
*/
|
|
131
|
+
const sendPhoneNumberOTP = (opts) => createAuthEndpoint("/phone-number/send-otp", {
|
|
132
|
+
method: "POST",
|
|
133
|
+
body: sendPhoneNumberOTPBodySchema,
|
|
134
|
+
metadata: { openapi: {
|
|
135
|
+
summary: "Send OTP to phone number",
|
|
136
|
+
description: "Use this endpoint to send OTP to phone number",
|
|
137
|
+
responses: { 200: {
|
|
138
|
+
description: "Success",
|
|
139
|
+
content: { "application/json": { schema: {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: { message: { type: "string" } }
|
|
142
|
+
} } }
|
|
143
|
+
} }
|
|
144
|
+
} }
|
|
145
|
+
}, async (ctx) => {
|
|
146
|
+
if (!opts?.sendOTP) {
|
|
147
|
+
ctx.context.logger.warn("sendOTP not implemented");
|
|
148
|
+
throw APIError.from("NOT_IMPLEMENTED", PHONE_NUMBER_ERROR_CODES.SEND_OTP_NOT_IMPLEMENTED);
|
|
149
|
+
}
|
|
150
|
+
if (opts.phoneNumberValidator) {
|
|
151
|
+
if (!await opts.phoneNumberValidator(ctx.body.phoneNumber)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER);
|
|
152
|
+
}
|
|
153
|
+
const code = generateOTP(opts.otpLength);
|
|
154
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
155
|
+
value: `${code}:0`,
|
|
156
|
+
identifier: ctx.body.phoneNumber,
|
|
157
|
+
expiresAt: getDate(opts.expiresIn, "sec")
|
|
158
|
+
});
|
|
159
|
+
const sendOTPResult = opts.sendOTP({
|
|
160
|
+
phoneNumber: ctx.body.phoneNumber,
|
|
161
|
+
code
|
|
162
|
+
}, ctx);
|
|
163
|
+
if (ctx.context.options.advanced?.backgroundTasks?.handler && sendOTPResult instanceof Promise) try {
|
|
164
|
+
ctx.context.runInBackground(sendOTPResult.catch((e) => {
|
|
165
|
+
ctx.context.logger.error("Failed to run background task:", e);
|
|
166
|
+
}));
|
|
167
|
+
} catch (e) {
|
|
168
|
+
ctx.context.logger.error("Failed to run background task:", e);
|
|
169
|
+
}
|
|
170
|
+
else await sendOTPResult;
|
|
171
|
+
return ctx.json({ message: "code sent" });
|
|
172
|
+
});
|
|
173
|
+
async function verifyAndConsumePhoneNumberOTP(ctx, opts, body) {
|
|
174
|
+
const { phoneNumber, code } = body;
|
|
175
|
+
if (opts.verifyOTP) {
|
|
176
|
+
if (!await opts.verifyOTP({
|
|
177
|
+
phoneNumber,
|
|
178
|
+
code
|
|
179
|
+
}, ctx)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
|
|
180
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(phoneNumber);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
await verifyPhoneNumberOTP(ctx, opts, phoneNumber, code);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* ### API Methods
|
|
187
|
+
*
|
|
188
|
+
* **server:**
|
|
189
|
+
* `auth.api.consumePhoneNumberOTP`
|
|
190
|
+
*
|
|
191
|
+
* Verifies and consumes an OTP without updating a user or creating a session.
|
|
192
|
+
*/
|
|
193
|
+
const consumePhoneNumberOTP = (opts) => createAuthEndpoint({
|
|
194
|
+
method: "POST",
|
|
195
|
+
body: consumePhoneNumberOTPBodySchema,
|
|
196
|
+
metadata: HIDE_METADATA
|
|
197
|
+
}, async (ctx) => {
|
|
198
|
+
await verifyAndConsumePhoneNumberOTP(ctx, opts, ctx.body);
|
|
199
|
+
return ctx.json({ status: true });
|
|
200
|
+
});
|
|
201
|
+
const verifyPhoneNumberBodySchema = z.object({
|
|
202
|
+
/**
|
|
203
|
+
* Phone number
|
|
204
|
+
*/
|
|
205
|
+
phoneNumber: z.string().meta({ description: "Phone number to verify. Eg: \"+1234567890\"" }),
|
|
206
|
+
/**
|
|
207
|
+
* OTP code
|
|
208
|
+
*/
|
|
209
|
+
code: z.string().meta({ description: "OTP code. Eg: \"123456\"" }),
|
|
210
|
+
/**
|
|
211
|
+
* Disable session creation after verification
|
|
212
|
+
* @default false
|
|
213
|
+
*/
|
|
214
|
+
disableSession: z.boolean().meta({ description: "Disable session creation after verification. Eg: false" }).optional(),
|
|
215
|
+
/**
|
|
216
|
+
* This checks if there is a session already
|
|
217
|
+
* and updates the phone number with the provided
|
|
218
|
+
* phone number
|
|
219
|
+
*/
|
|
220
|
+
updatePhoneNumber: z.boolean().meta({ description: "Check if there is a session and update the phone number. Eg: true" }).optional()
|
|
221
|
+
}).and(z.record(z.string(), z.any()));
|
|
222
|
+
/**
|
|
223
|
+
* ### Endpoint
|
|
224
|
+
*
|
|
225
|
+
* POST `/phone-number/verify`
|
|
226
|
+
*
|
|
227
|
+
* ### API Methods
|
|
228
|
+
*
|
|
229
|
+
* **server:**
|
|
230
|
+
* `auth.api.verifyPhoneNumber`
|
|
231
|
+
*
|
|
232
|
+
* **client:**
|
|
233
|
+
* `authClient.phoneNumber.verify`
|
|
234
|
+
*
|
|
235
|
+
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/phone-number#api-method-phone-number-verify)
|
|
236
|
+
*/
|
|
237
|
+
const verifyPhoneNumber = (opts) => createAuthEndpoint("/phone-number/verify", {
|
|
238
|
+
method: "POST",
|
|
239
|
+
body: verifyPhoneNumberBodySchema,
|
|
240
|
+
metadata: { openapi: {
|
|
241
|
+
summary: "Verify phone number",
|
|
242
|
+
description: "Use this endpoint to verify phone number",
|
|
243
|
+
responses: {
|
|
244
|
+
"200": {
|
|
245
|
+
description: "Phone number verified successfully",
|
|
246
|
+
content: { "application/json": { schema: {
|
|
247
|
+
type: "object",
|
|
248
|
+
properties: {
|
|
249
|
+
status: {
|
|
250
|
+
type: "boolean",
|
|
251
|
+
description: "Indicates if the verification was successful",
|
|
252
|
+
enum: [true]
|
|
253
|
+
},
|
|
254
|
+
token: {
|
|
255
|
+
type: "string",
|
|
256
|
+
nullable: true,
|
|
257
|
+
description: "Session token if session is created, null if disableSession is true or no session is created"
|
|
258
|
+
},
|
|
259
|
+
user: {
|
|
260
|
+
type: "object",
|
|
261
|
+
nullable: true,
|
|
262
|
+
properties: {
|
|
263
|
+
id: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "Unique identifier of the user"
|
|
266
|
+
},
|
|
267
|
+
email: {
|
|
268
|
+
type: "string",
|
|
269
|
+
format: "email",
|
|
270
|
+
nullable: true,
|
|
271
|
+
description: "User's email address"
|
|
272
|
+
},
|
|
273
|
+
emailVerified: {
|
|
274
|
+
type: "boolean",
|
|
275
|
+
nullable: true,
|
|
276
|
+
description: "Whether the email is verified"
|
|
277
|
+
},
|
|
278
|
+
name: {
|
|
279
|
+
type: "string",
|
|
280
|
+
nullable: true,
|
|
281
|
+
description: "User's name"
|
|
282
|
+
},
|
|
283
|
+
image: {
|
|
284
|
+
type: "string",
|
|
285
|
+
format: "uri",
|
|
286
|
+
nullable: true,
|
|
287
|
+
description: "User's profile image URL"
|
|
288
|
+
},
|
|
289
|
+
phoneNumber: {
|
|
290
|
+
type: "string",
|
|
291
|
+
description: "User's phone number"
|
|
292
|
+
},
|
|
293
|
+
phoneNumberVerified: {
|
|
294
|
+
type: "boolean",
|
|
295
|
+
description: "Whether the phone number is verified"
|
|
296
|
+
},
|
|
297
|
+
createdAt: {
|
|
298
|
+
type: "string",
|
|
299
|
+
format: "date-time",
|
|
300
|
+
description: "Timestamp when the user was created"
|
|
301
|
+
},
|
|
302
|
+
updatedAt: {
|
|
303
|
+
type: "string",
|
|
304
|
+
format: "date-time",
|
|
305
|
+
description: "Timestamp when the user was last updated"
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
required: [
|
|
309
|
+
"id",
|
|
310
|
+
"phoneNumber",
|
|
311
|
+
"phoneNumberVerified",
|
|
312
|
+
"createdAt",
|
|
313
|
+
"updatedAt"
|
|
314
|
+
],
|
|
315
|
+
description: "User object with phone number details, null if no user is created or found"
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
required: ["status"]
|
|
319
|
+
} } }
|
|
320
|
+
},
|
|
321
|
+
400: { description: "Invalid OTP" }
|
|
322
|
+
}
|
|
323
|
+
} }
|
|
324
|
+
}, async (ctx) => {
|
|
325
|
+
await verifyAndConsumePhoneNumberOTP(ctx, opts, ctx.body);
|
|
326
|
+
if (ctx.body.updatePhoneNumber) {
|
|
327
|
+
const session = await getSessionFromCtx(ctx);
|
|
328
|
+
if (!session) throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.USER_NOT_FOUND);
|
|
329
|
+
if ((await ctx.context.adapter.findMany({
|
|
330
|
+
model: "user",
|
|
331
|
+
where: [{
|
|
332
|
+
field: "phoneNumber",
|
|
333
|
+
value: ctx.body.phoneNumber
|
|
334
|
+
}]
|
|
335
|
+
})).length) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.PHONE_NUMBER_EXIST);
|
|
336
|
+
const user = await ctx.context.internalAdapter.updateUser(session.user.id, {
|
|
337
|
+
[opts.phoneNumber]: ctx.body.phoneNumber,
|
|
338
|
+
[opts.phoneNumberVerified]: true
|
|
339
|
+
});
|
|
340
|
+
if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_UPDATE_USER);
|
|
341
|
+
await opts?.callbackOnVerification?.({
|
|
342
|
+
phoneNumber: ctx.body.phoneNumber,
|
|
343
|
+
user
|
|
344
|
+
}, ctx);
|
|
345
|
+
return ctx.json({
|
|
346
|
+
status: true,
|
|
347
|
+
token: session.session.token,
|
|
348
|
+
user: parseUserOutput(ctx.context.options, user)
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
let user = await ctx.context.adapter.findOne({
|
|
352
|
+
model: "user",
|
|
353
|
+
where: [{
|
|
354
|
+
value: ctx.body.phoneNumber,
|
|
355
|
+
field: opts.phoneNumber
|
|
356
|
+
}]
|
|
357
|
+
});
|
|
358
|
+
if (!user) {
|
|
359
|
+
if (opts?.signUpOnVerification) {
|
|
360
|
+
const { phoneNumber, code, disableSession, updatePhoneNumber, ...rest } = ctx.body;
|
|
361
|
+
const additionalFields = parseUserInput(ctx.context.options, rest, "create");
|
|
362
|
+
user = await ctx.context.internalAdapter.createUser({
|
|
363
|
+
...additionalFields,
|
|
364
|
+
email: opts.signUpOnVerification.getTempEmail(ctx.body.phoneNumber),
|
|
365
|
+
name: opts.signUpOnVerification.getTempName ? opts.signUpOnVerification.getTempName(ctx.body.phoneNumber) : ctx.body.phoneNumber,
|
|
366
|
+
[opts.phoneNumber]: ctx.body.phoneNumber,
|
|
367
|
+
[opts.phoneNumberVerified]: true
|
|
368
|
+
}, { method: "phone-number" });
|
|
369
|
+
if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_CREATE_USER);
|
|
370
|
+
}
|
|
371
|
+
} else user = await ctx.context.internalAdapter.updateUser(user.id, { [opts.phoneNumberVerified]: true });
|
|
372
|
+
if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_UPDATE_USER);
|
|
373
|
+
await opts?.callbackOnVerification?.({
|
|
374
|
+
phoneNumber: ctx.body.phoneNumber,
|
|
375
|
+
user
|
|
376
|
+
}, ctx);
|
|
377
|
+
if (!ctx.body.disableSession) {
|
|
378
|
+
const session = await ctx.context.internalAdapter.createSession(user.id);
|
|
379
|
+
if (!session) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
|
|
380
|
+
await setSessionCookie(ctx, {
|
|
381
|
+
session,
|
|
382
|
+
user
|
|
383
|
+
});
|
|
384
|
+
return ctx.json({
|
|
385
|
+
status: true,
|
|
386
|
+
token: session.token,
|
|
387
|
+
user: parseUserOutput(ctx.context.options, user)
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return ctx.json({
|
|
391
|
+
status: true,
|
|
392
|
+
token: null,
|
|
393
|
+
user: parseUserOutput(ctx.context.options, user)
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
const requestPasswordResetPhoneNumberBodySchema = z.object({ phoneNumber: z.string() });
|
|
397
|
+
const requestPasswordResetPhoneNumber = (opts) => createAuthEndpoint("/phone-number/request-password-reset", {
|
|
398
|
+
method: "POST",
|
|
399
|
+
body: requestPasswordResetPhoneNumberBodySchema,
|
|
400
|
+
metadata: { openapi: {
|
|
401
|
+
description: "Request OTP for password reset via phone number",
|
|
402
|
+
responses: { "200": {
|
|
403
|
+
description: "OTP sent successfully for password reset",
|
|
404
|
+
content: { "application/json": { schema: {
|
|
405
|
+
type: "object",
|
|
406
|
+
properties: { status: {
|
|
407
|
+
type: "boolean",
|
|
408
|
+
description: "Indicates if the OTP was sent successfully",
|
|
409
|
+
enum: [true]
|
|
410
|
+
} },
|
|
411
|
+
required: ["status"]
|
|
412
|
+
} } }
|
|
413
|
+
} }
|
|
414
|
+
} }
|
|
415
|
+
}, async (ctx) => {
|
|
416
|
+
const user = await ctx.context.adapter.findOne({
|
|
417
|
+
model: "user",
|
|
418
|
+
where: [{
|
|
419
|
+
value: ctx.body.phoneNumber,
|
|
420
|
+
field: opts.phoneNumber
|
|
421
|
+
}]
|
|
422
|
+
});
|
|
423
|
+
const code = generateOTP(opts.otpLength);
|
|
424
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
425
|
+
value: `${code}:0`,
|
|
426
|
+
identifier: `${ctx.body.phoneNumber}-request-password-reset`,
|
|
427
|
+
expiresAt: getDate(opts.expiresIn, "sec")
|
|
428
|
+
});
|
|
429
|
+
if (!user) return ctx.json({ status: true });
|
|
430
|
+
if (opts.sendPasswordResetOTP) await ctx.context.runInBackgroundOrAwait(opts.sendPasswordResetOTP({
|
|
431
|
+
phoneNumber: ctx.body.phoneNumber,
|
|
432
|
+
code
|
|
433
|
+
}, ctx));
|
|
434
|
+
return ctx.json({ status: true });
|
|
435
|
+
});
|
|
436
|
+
const resetPasswordPhoneNumberBodySchema = z.object({
|
|
437
|
+
otp: z.string().meta({ description: "The one time password to reset the password. Eg: \"123456\"" }),
|
|
438
|
+
phoneNumber: z.string().meta({ description: "The phone number to the account which intends to reset the password for. Eg: \"+1234567890\"" }),
|
|
439
|
+
newPassword: z.string().meta({ description: `The new password. Eg: "new-and-secure-password"` })
|
|
440
|
+
});
|
|
441
|
+
const resetPasswordPhoneNumber = (opts) => createAuthEndpoint("/phone-number/reset-password", {
|
|
442
|
+
method: "POST",
|
|
443
|
+
body: resetPasswordPhoneNumberBodySchema,
|
|
444
|
+
metadata: { openapi: {
|
|
445
|
+
description: "Reset password using phone number OTP",
|
|
446
|
+
responses: { "200": {
|
|
447
|
+
description: "Password reset successfully",
|
|
448
|
+
content: { "application/json": { schema: {
|
|
449
|
+
type: "object",
|
|
450
|
+
properties: { status: {
|
|
451
|
+
type: "boolean",
|
|
452
|
+
description: "Indicates if the password was reset successfully",
|
|
453
|
+
enum: [true]
|
|
454
|
+
} },
|
|
455
|
+
required: ["status"]
|
|
456
|
+
} } }
|
|
457
|
+
} }
|
|
458
|
+
} }
|
|
459
|
+
}, async (ctx) => {
|
|
460
|
+
await verifyPhoneNumberOTP(ctx, opts, `${ctx.body.phoneNumber}-request-password-reset`, ctx.body.otp);
|
|
461
|
+
const user = await ctx.context.adapter.findOne({
|
|
462
|
+
model: "user",
|
|
463
|
+
where: [{
|
|
464
|
+
field: "phoneNumber",
|
|
465
|
+
value: ctx.body.phoneNumber
|
|
466
|
+
}]
|
|
467
|
+
});
|
|
468
|
+
if (!user) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.UNEXPECTED_ERROR);
|
|
469
|
+
const minLength = ctx.context.password.config.minPasswordLength;
|
|
470
|
+
const maxLength = ctx.context.password.config.maxPasswordLength;
|
|
471
|
+
if (ctx.body.newPassword.length < minLength) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
472
|
+
if (ctx.body.newPassword.length > maxLength) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
473
|
+
const hashedPassword = await ctx.context.password.hash(ctx.body.newPassword);
|
|
474
|
+
if (!await ctx.context.internalAdapter.findCredentialAccount(user.id)) await ctx.context.internalAdapter.createAccount({
|
|
475
|
+
userId: user.id,
|
|
476
|
+
providerId: "credential",
|
|
477
|
+
accountId: user.id,
|
|
478
|
+
password: hashedPassword
|
|
479
|
+
});
|
|
480
|
+
else await ctx.context.internalAdapter.updatePassword(user.id, hashedPassword);
|
|
481
|
+
if (ctx.context.options.emailAndPassword?.onPasswordReset) await ctx.context.options.emailAndPassword.onPasswordReset({ user }, ctx.request);
|
|
482
|
+
if (ctx.context.options.emailAndPassword?.revokeSessionsOnPasswordReset) await ctx.context.internalAdapter.deleteUserSessions(user.id);
|
|
483
|
+
return ctx.json({ status: true });
|
|
484
|
+
});
|
|
485
|
+
/**
|
|
486
|
+
* Atomically verifies a phone-number OTP against a stored verification value.
|
|
487
|
+
*
|
|
488
|
+
* Consuming the row is the race gate: the first concurrent caller wins the
|
|
489
|
+
* row, every racer behind it gets `null` and is rejected, so the same code can
|
|
490
|
+
* never satisfy two simultaneous verifications. On a wrong code that is still
|
|
491
|
+
* within the attempt budget, the row is recreated with the same value and
|
|
492
|
+
* expiry and an incremented attempt counter. Once the budget is exhausted the
|
|
493
|
+
* row is not recreated.
|
|
494
|
+
*/
|
|
495
|
+
async function verifyPhoneNumberOTP(ctx, opts, identifier, providedCode) {
|
|
496
|
+
const existing = await ctx.context.internalAdapter.findVerificationValue(identifier);
|
|
497
|
+
if (!existing) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.OTP_NOT_FOUND);
|
|
498
|
+
if (existing.expiresAt < /* @__PURE__ */ new Date()) {
|
|
499
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(identifier);
|
|
500
|
+
throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.OTP_EXPIRED);
|
|
501
|
+
}
|
|
502
|
+
const allowedAttempts = opts?.allowedAttempts ?? 3;
|
|
503
|
+
if (parseVerificationAttempts(existing.value.split(":")[1]) >= allowedAttempts) {
|
|
504
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(identifier);
|
|
505
|
+
throw APIError.from("FORBIDDEN", PHONE_NUMBER_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
506
|
+
}
|
|
507
|
+
const consumed = await ctx.context.internalAdapter.consumeVerificationValue(identifier);
|
|
508
|
+
if (!consumed) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
|
|
509
|
+
const [otpValue, rawAttempts] = consumed.value.split(":");
|
|
510
|
+
const attempts = parseVerificationAttempts(rawAttempts);
|
|
511
|
+
if (attempts >= allowedAttempts) throw APIError.from("FORBIDDEN", PHONE_NUMBER_ERROR_CODES.TOO_MANY_ATTEMPTS);
|
|
512
|
+
if (otpValue !== providedCode) {
|
|
513
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
514
|
+
value: `${otpValue}:${attempts + 1}`,
|
|
515
|
+
identifier,
|
|
516
|
+
expiresAt: consumed.expiresAt
|
|
517
|
+
});
|
|
518
|
+
throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function parseVerificationAttempts(value) {
|
|
522
|
+
const attempts = Number(value ?? 0);
|
|
523
|
+
return Number.isSafeInteger(attempts) && attempts > 0 ? attempts : 0;
|
|
524
|
+
}
|
|
525
|
+
function generateOTP(size) {
|
|
526
|
+
return generateRandomString(size, "0-9");
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
export { consumePhoneNumberOTP, requestPasswordResetPhoneNumber, resetPasswordPhoneNumber, sendPhoneNumberOTP, signInPhoneNumber, verifyPhoneNumber };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/plugins/phone-number/schema.d.ts
|
|
2
|
+
declare const schema: {
|
|
3
|
+
user: {
|
|
4
|
+
fields: {
|
|
5
|
+
phoneNumber: {
|
|
6
|
+
type: "string";
|
|
7
|
+
required: false;
|
|
8
|
+
unique: true;
|
|
9
|
+
sortable: true;
|
|
10
|
+
returned: true;
|
|
11
|
+
};
|
|
12
|
+
phoneNumberVerified: {
|
|
13
|
+
type: "boolean";
|
|
14
|
+
required: false;
|
|
15
|
+
returned: true;
|
|
16
|
+
input: false;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { schema };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/plugins/phone-number/schema.ts
|
|
2
|
+
const schema = { user: { fields: {
|
|
3
|
+
phoneNumber: {
|
|
4
|
+
type: "string",
|
|
5
|
+
required: false,
|
|
6
|
+
unique: true,
|
|
7
|
+
sortable: true,
|
|
8
|
+
returned: true
|
|
9
|
+
},
|
|
10
|
+
phoneNumberVerified: {
|
|
11
|
+
type: "boolean",
|
|
12
|
+
required: false,
|
|
13
|
+
returned: true,
|
|
14
|
+
input: false
|
|
15
|
+
}
|
|
16
|
+
} } };
|
|
17
|
+
//#endregion
|
|
18
|
+
export { schema };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { User } from "../../types/models.mjs";
|
|
2
|
+
import { InferOptionSchema } from "../../types/plugins.mjs";
|
|
3
|
+
import { schema } from "./schema.mjs";
|
|
4
|
+
import { Awaitable, GenericEndpointContext } from "@better-auth/core";
|
|
5
|
+
|
|
6
|
+
//#region src/plugins/phone-number/types.d.ts
|
|
7
|
+
interface UserWithPhoneNumber extends User {
|
|
8
|
+
phoneNumber: string;
|
|
9
|
+
phoneNumberVerified: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface PhoneNumberOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Length of the OTP code
|
|
14
|
+
* @default 6
|
|
15
|
+
*/
|
|
16
|
+
otpLength?: number | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Send OTP code to the user
|
|
19
|
+
*
|
|
20
|
+
* @param phoneNumber
|
|
21
|
+
* @param code
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
sendOTP: (data: {
|
|
25
|
+
phoneNumber: string;
|
|
26
|
+
code: string;
|
|
27
|
+
}, ctx?: GenericEndpointContext | undefined) => Awaitable<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Custom OTP verification function
|
|
30
|
+
*
|
|
31
|
+
* If provided, this function will be called instead of the internal verification logic.
|
|
32
|
+
* This is useful when using SMS providers that handle their own OTP generation and verification.
|
|
33
|
+
*
|
|
34
|
+
* @param data - Contains phone number and OTP code
|
|
35
|
+
* @param request - The request object
|
|
36
|
+
* @returns true if OTP is valid, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
verifyOTP?: ((data: {
|
|
39
|
+
phoneNumber: string;
|
|
40
|
+
code: string;
|
|
41
|
+
}, ctx?: GenericEndpointContext) => Awaitable<boolean>) | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* a callback to send otp on user requesting to reset their password
|
|
44
|
+
*
|
|
45
|
+
* @param data - contains phone number and code
|
|
46
|
+
* @param request - the request object
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
sendPasswordResetOTP?: ((data: {
|
|
50
|
+
phoneNumber: string;
|
|
51
|
+
code: string;
|
|
52
|
+
}, ctx?: GenericEndpointContext) => Awaitable<void>) | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Expiry time of the OTP code in seconds
|
|
55
|
+
* @default 300
|
|
56
|
+
*/
|
|
57
|
+
expiresIn?: number | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Function to validate phone number
|
|
60
|
+
*
|
|
61
|
+
* by default any string is accepted
|
|
62
|
+
*/
|
|
63
|
+
phoneNumberValidator?: ((phoneNumber: string) => Awaitable<boolean>) | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Require a phone number verification before signing in
|
|
66
|
+
*
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
requireVerification?: boolean | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Callback when phone number is verified
|
|
72
|
+
*/
|
|
73
|
+
callbackOnVerification?: ((data: {
|
|
74
|
+
phoneNumber: string;
|
|
75
|
+
user: UserWithPhoneNumber;
|
|
76
|
+
}, ctx?: GenericEndpointContext) => Awaitable<void>) | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Sign up user after phone number verification
|
|
79
|
+
*
|
|
80
|
+
* the user will be signed up with the temporary email
|
|
81
|
+
* and the phone number will be updated after verification
|
|
82
|
+
*/
|
|
83
|
+
signUpOnVerification?: {
|
|
84
|
+
/**
|
|
85
|
+
* When a user signs up, a temporary email will be need to be created
|
|
86
|
+
* to sign up the user. This function should return a temporary email
|
|
87
|
+
* for the user given the phone number
|
|
88
|
+
*
|
|
89
|
+
* @param phoneNumber
|
|
90
|
+
* @returns string (temporary email)
|
|
91
|
+
*/
|
|
92
|
+
getTempEmail: (phoneNumber: string) => string;
|
|
93
|
+
/**
|
|
94
|
+
* When a user signs up, a temporary name will be need to be created
|
|
95
|
+
* to sign up the user. This function should return a temporary name
|
|
96
|
+
* for the user given the phone number
|
|
97
|
+
*
|
|
98
|
+
* @param phoneNumber
|
|
99
|
+
* @returns string (temporary name)
|
|
100
|
+
*
|
|
101
|
+
* @default phoneNumber - the phone number will be used as the name
|
|
102
|
+
*/
|
|
103
|
+
getTempName?: (phoneNumber: string) => string;
|
|
104
|
+
} | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Custom schema for the admin plugin
|
|
107
|
+
*/
|
|
108
|
+
schema?: InferOptionSchema<typeof schema> | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Allowed attempts for the OTP code
|
|
111
|
+
* @default 3
|
|
112
|
+
*/
|
|
113
|
+
allowedAttempts?: number | undefined;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
export { PhoneNumberOptions, UserWithPhoneNumber };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { siwe } from "./index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/siwe/client.d.ts
|
|
4
|
+
declare const siweClient: () => {
|
|
5
|
+
id: "siwe";
|
|
6
|
+
version: string;
|
|
7
|
+
$InferServerPlugin: ReturnType<typeof siwe>;
|
|
8
|
+
pathMethods: {
|
|
9
|
+
"/siwe/nonce": "POST";
|
|
10
|
+
"/siwe/get-nonce": "POST";
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { siweClient };
|