@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,522 @@
|
|
|
1
|
+
import { originCheck } from "../middlewares/origin-check.mjs";
|
|
2
|
+
import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
|
|
3
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
|
+
import { deleteSessionCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
5
|
+
import { getSessionFromCtx, isStateful, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs";
|
|
6
|
+
import { createEmailVerificationToken } from "./email-verification.mjs";
|
|
7
|
+
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
8
|
+
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
9
|
+
import * as z from "zod";
|
|
10
|
+
//#region src/api/routes/update-user.ts
|
|
11
|
+
const updateUserBodySchema = z.record(z.string().meta({ description: "Field name must be a string" }), z.any());
|
|
12
|
+
const updateUser = () => createAuthEndpoint("/update-user", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
operationId: "updateUser",
|
|
15
|
+
body: updateUserBodySchema,
|
|
16
|
+
use: [sessionMiddleware],
|
|
17
|
+
metadata: {
|
|
18
|
+
$Infer: { body: {} },
|
|
19
|
+
openapi: {
|
|
20
|
+
operationId: "updateUser",
|
|
21
|
+
description: "Update the current user",
|
|
22
|
+
requestBody: { content: { "application/json": { schema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
name: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "The name of the user"
|
|
28
|
+
},
|
|
29
|
+
image: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "The image of the user",
|
|
32
|
+
nullable: true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} } } },
|
|
36
|
+
responses: { "200": {
|
|
37
|
+
description: "Success",
|
|
38
|
+
content: { "application/json": { schema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: { user: {
|
|
41
|
+
type: "object",
|
|
42
|
+
$ref: "#/components/schemas/User"
|
|
43
|
+
} }
|
|
44
|
+
} } }
|
|
45
|
+
} }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, async (ctx) => {
|
|
49
|
+
const body = ctx.body;
|
|
50
|
+
if (typeof body !== "object" || Array.isArray(body)) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.BODY_MUST_BE_AN_OBJECT);
|
|
51
|
+
if (body.email) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.EMAIL_CAN_NOT_BE_UPDATED);
|
|
52
|
+
const { name, image, ...rest } = body;
|
|
53
|
+
const session = ctx.context.session;
|
|
54
|
+
const additionalFields = parseUserInput(ctx.context.options, rest, "update");
|
|
55
|
+
if (image === void 0 && name === void 0 && Object.keys(additionalFields).length === 0) throw APIError.fromStatus("BAD_REQUEST", { message: "No fields to update" });
|
|
56
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, {
|
|
57
|
+
name,
|
|
58
|
+
image,
|
|
59
|
+
...additionalFields
|
|
60
|
+
}) ?? {
|
|
61
|
+
...session.user,
|
|
62
|
+
...name !== void 0 && { name },
|
|
63
|
+
...image !== void 0 && { image },
|
|
64
|
+
...additionalFields
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Update the session cookie with the new user data
|
|
68
|
+
*/
|
|
69
|
+
await setSessionCookie(ctx, {
|
|
70
|
+
session: session.session,
|
|
71
|
+
user: updatedUser
|
|
72
|
+
});
|
|
73
|
+
return ctx.json({ status: true });
|
|
74
|
+
});
|
|
75
|
+
const changePassword = createAuthEndpoint("/change-password", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
operationId: "changePassword",
|
|
78
|
+
body: z.object({
|
|
79
|
+
/**
|
|
80
|
+
* The new password to set
|
|
81
|
+
*/
|
|
82
|
+
newPassword: z.string().meta({ description: "The new password to set" }),
|
|
83
|
+
/**
|
|
84
|
+
* The current password of the user
|
|
85
|
+
*/
|
|
86
|
+
currentPassword: z.string().meta({ description: "The current password is required" }),
|
|
87
|
+
/**
|
|
88
|
+
* revoke all sessions that are not the
|
|
89
|
+
* current one logged in by the user
|
|
90
|
+
*/
|
|
91
|
+
revokeOtherSessions: z.boolean().meta({ description: "Must be a boolean value" }).optional()
|
|
92
|
+
}),
|
|
93
|
+
use: [sensitiveSessionMiddleware],
|
|
94
|
+
metadata: { openapi: {
|
|
95
|
+
operationId: "changePassword",
|
|
96
|
+
description: "Change the password of the user",
|
|
97
|
+
responses: { "200": {
|
|
98
|
+
description: "Password successfully changed",
|
|
99
|
+
content: { "application/json": { schema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
token: {
|
|
103
|
+
type: "string",
|
|
104
|
+
nullable: true,
|
|
105
|
+
description: "New session token if other sessions were revoked"
|
|
106
|
+
},
|
|
107
|
+
user: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {
|
|
110
|
+
id: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "The unique identifier of the user"
|
|
113
|
+
},
|
|
114
|
+
email: {
|
|
115
|
+
type: "string",
|
|
116
|
+
format: "email",
|
|
117
|
+
description: "The email address of the user"
|
|
118
|
+
},
|
|
119
|
+
name: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "The name of the user"
|
|
122
|
+
},
|
|
123
|
+
image: {
|
|
124
|
+
type: "string",
|
|
125
|
+
format: "uri",
|
|
126
|
+
nullable: true,
|
|
127
|
+
description: "The profile image URL of the user"
|
|
128
|
+
},
|
|
129
|
+
emailVerified: {
|
|
130
|
+
type: "boolean",
|
|
131
|
+
description: "Whether the email has been verified"
|
|
132
|
+
},
|
|
133
|
+
createdAt: {
|
|
134
|
+
type: "string",
|
|
135
|
+
format: "date-time",
|
|
136
|
+
description: "When the user was created"
|
|
137
|
+
},
|
|
138
|
+
updatedAt: {
|
|
139
|
+
type: "string",
|
|
140
|
+
format: "date-time",
|
|
141
|
+
description: "When the user was last updated"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
required: [
|
|
145
|
+
"id",
|
|
146
|
+
"email",
|
|
147
|
+
"name",
|
|
148
|
+
"emailVerified",
|
|
149
|
+
"createdAt",
|
|
150
|
+
"updatedAt"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
required: ["user"]
|
|
155
|
+
} } }
|
|
156
|
+
} }
|
|
157
|
+
} }
|
|
158
|
+
}, async (ctx) => {
|
|
159
|
+
const { newPassword, currentPassword, revokeOtherSessions } = ctx.body;
|
|
160
|
+
const session = ctx.context.session;
|
|
161
|
+
const minPasswordLength = ctx.context.password.config.minPasswordLength;
|
|
162
|
+
if (newPassword.length < minPasswordLength) {
|
|
163
|
+
ctx.context.logger.warn("Password is too short");
|
|
164
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
165
|
+
}
|
|
166
|
+
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
167
|
+
if (newPassword.length > maxPasswordLength) {
|
|
168
|
+
ctx.context.logger.warn("Password is too long");
|
|
169
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
170
|
+
}
|
|
171
|
+
const account = await ctx.context.internalAdapter.findCredentialAccount(session.user.id);
|
|
172
|
+
if (!account || !account.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND);
|
|
173
|
+
const passwordHash = await ctx.context.password.hash(newPassword);
|
|
174
|
+
if (!await ctx.context.password.verify({
|
|
175
|
+
hash: account.password,
|
|
176
|
+
password: currentPassword
|
|
177
|
+
})) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
|
|
178
|
+
await ctx.context.internalAdapter.updateAccount(account.id, { password: passwordHash });
|
|
179
|
+
let token = null;
|
|
180
|
+
if (revokeOtherSessions) {
|
|
181
|
+
await ctx.context.internalAdapter.deleteUserSessions(session.user.id);
|
|
182
|
+
const newSession = await ctx.context.internalAdapter.createSession(session.user.id);
|
|
183
|
+
if (!newSession) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_GET_SESSION);
|
|
184
|
+
await setSessionCookie(ctx, {
|
|
185
|
+
session: newSession,
|
|
186
|
+
user: session.user
|
|
187
|
+
});
|
|
188
|
+
token = newSession.token;
|
|
189
|
+
}
|
|
190
|
+
return ctx.json({
|
|
191
|
+
token,
|
|
192
|
+
user: parseUserOutput(ctx.context.options, session.user)
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
const setPassword = createAuthEndpoint.serverOnly({
|
|
196
|
+
method: "POST",
|
|
197
|
+
body: z.object({
|
|
198
|
+
/**
|
|
199
|
+
* The new password to set
|
|
200
|
+
*/
|
|
201
|
+
newPassword: z.string().meta({ description: "The new password to set is required" }) }),
|
|
202
|
+
use: [sensitiveSessionMiddleware]
|
|
203
|
+
}, async (ctx) => {
|
|
204
|
+
const { newPassword } = ctx.body;
|
|
205
|
+
const session = ctx.context.session;
|
|
206
|
+
const minPasswordLength = ctx.context.password.config.minPasswordLength;
|
|
207
|
+
if (newPassword.length < minPasswordLength) {
|
|
208
|
+
ctx.context.logger.warn("Password is too short");
|
|
209
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
210
|
+
}
|
|
211
|
+
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
212
|
+
if (newPassword.length > maxPasswordLength) {
|
|
213
|
+
ctx.context.logger.warn("Password is too long");
|
|
214
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
215
|
+
}
|
|
216
|
+
const account = await ctx.context.internalAdapter.findCredentialAccount(session.user.id);
|
|
217
|
+
const passwordHash = await ctx.context.password.hash(newPassword);
|
|
218
|
+
if (!account) {
|
|
219
|
+
await ctx.context.internalAdapter.linkAccount({
|
|
220
|
+
userId: session.user.id,
|
|
221
|
+
providerId: "credential",
|
|
222
|
+
accountId: session.user.id,
|
|
223
|
+
password: passwordHash
|
|
224
|
+
});
|
|
225
|
+
return ctx.json({ status: true });
|
|
226
|
+
}
|
|
227
|
+
if (!account.password) {
|
|
228
|
+
await ctx.context.internalAdapter.updateAccount(account.id, { password: passwordHash });
|
|
229
|
+
return ctx.json({ status: true });
|
|
230
|
+
}
|
|
231
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_ALREADY_SET);
|
|
232
|
+
});
|
|
233
|
+
const deleteUser = createAuthEndpoint("/delete-user", {
|
|
234
|
+
method: "POST",
|
|
235
|
+
use: [sensitiveSessionMiddleware],
|
|
236
|
+
body: z.object({
|
|
237
|
+
/**
|
|
238
|
+
* The callback URL to redirect to after the user is deleted
|
|
239
|
+
* this is only used on delete user callback
|
|
240
|
+
*/
|
|
241
|
+
callbackURL: z.string().meta({ description: "The callback URL to redirect to after the user is deleted" }).optional(),
|
|
242
|
+
/**
|
|
243
|
+
* The password of the user. If the password isn't provided, session freshness
|
|
244
|
+
* will be checked.
|
|
245
|
+
*/
|
|
246
|
+
password: z.string().meta({ description: "The password of the user is required to delete the user" }).optional(),
|
|
247
|
+
/**
|
|
248
|
+
* The token to delete the user. If the token is provided, the user will be deleted
|
|
249
|
+
*/
|
|
250
|
+
token: z.string().meta({ description: "The token to delete the user is required" }).optional()
|
|
251
|
+
}),
|
|
252
|
+
metadata: { openapi: {
|
|
253
|
+
operationId: "deleteUser",
|
|
254
|
+
description: "Delete the user",
|
|
255
|
+
requestBody: { content: { "application/json": { schema: {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
callbackURL: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "The callback URL to redirect to after the user is deleted"
|
|
261
|
+
},
|
|
262
|
+
password: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "The user's password. Required if session is not fresh"
|
|
265
|
+
},
|
|
266
|
+
token: {
|
|
267
|
+
type: "string",
|
|
268
|
+
description: "The deletion verification token"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} } } },
|
|
272
|
+
responses: { "200": {
|
|
273
|
+
description: "User deletion processed successfully",
|
|
274
|
+
content: { "application/json": { schema: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
success: {
|
|
278
|
+
type: "boolean",
|
|
279
|
+
description: "Indicates if the operation was successful"
|
|
280
|
+
},
|
|
281
|
+
message: {
|
|
282
|
+
type: "string",
|
|
283
|
+
enum: ["User deleted", "Verification email sent"],
|
|
284
|
+
description: "Status message of the deletion process"
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
required: ["success", "message"]
|
|
288
|
+
} } }
|
|
289
|
+
} }
|
|
290
|
+
} }
|
|
291
|
+
}, async (ctx) => {
|
|
292
|
+
if (!ctx.context.options.user?.deleteUser?.enabled) {
|
|
293
|
+
ctx.context.logger.error("Delete user is disabled. Enable it in the options");
|
|
294
|
+
throw APIError.fromStatus("NOT_FOUND");
|
|
295
|
+
}
|
|
296
|
+
const session = ctx.context.session;
|
|
297
|
+
if (ctx.body.password) {
|
|
298
|
+
const account = await ctx.context.internalAdapter.findCredentialAccount(session.user.id);
|
|
299
|
+
if (!account || !account.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND);
|
|
300
|
+
if (!await ctx.context.password.verify({
|
|
301
|
+
hash: account.password,
|
|
302
|
+
password: ctx.body.password
|
|
303
|
+
})) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
|
|
304
|
+
}
|
|
305
|
+
if (ctx.body.token) {
|
|
306
|
+
await deleteUserCallback({
|
|
307
|
+
...ctx,
|
|
308
|
+
query: { token: ctx.body.token }
|
|
309
|
+
});
|
|
310
|
+
return ctx.json({
|
|
311
|
+
success: true,
|
|
312
|
+
message: "User deleted"
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (ctx.context.options.user.deleteUser?.sendDeleteAccountVerification) {
|
|
316
|
+
const token = generateRandomString(32, "0-9", "a-z");
|
|
317
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
318
|
+
value: session.user.id,
|
|
319
|
+
identifier: `delete-account-${token}`,
|
|
320
|
+
expiresAt: new Date(Date.now() + (ctx.context.options.user.deleteUser?.deleteTokenExpiresIn || 3600 * 24) * 1e3)
|
|
321
|
+
});
|
|
322
|
+
const url = `${ctx.context.baseURL}/delete-user/callback?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
|
|
323
|
+
await ctx.context.runInBackgroundOrAwait(ctx.context.options.user.deleteUser.sendDeleteAccountVerification({
|
|
324
|
+
user: session.user,
|
|
325
|
+
url,
|
|
326
|
+
token
|
|
327
|
+
}, ctx.request));
|
|
328
|
+
return ctx.json({
|
|
329
|
+
success: true,
|
|
330
|
+
message: "Verification email sent"
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
if (!ctx.body.password && ctx.context.sessionConfig.freshAge !== 0) {
|
|
334
|
+
const createdAt = new Date(session.session.createdAt).getTime();
|
|
335
|
+
const freshAge = ctx.context.sessionConfig.freshAge * 1e3;
|
|
336
|
+
if (Date.now() - createdAt >= freshAge) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.SESSION_EXPIRED);
|
|
337
|
+
}
|
|
338
|
+
const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete;
|
|
339
|
+
if (beforeDelete) await beforeDelete(session.user, ctx.request);
|
|
340
|
+
await ctx.context.internalAdapter.deleteUser(session.user.id);
|
|
341
|
+
await ctx.context.internalAdapter.deleteUserSessions(session.user.id);
|
|
342
|
+
deleteSessionCookie(ctx);
|
|
343
|
+
const afterDelete = ctx.context.options.user.deleteUser?.afterDelete;
|
|
344
|
+
if (afterDelete) await afterDelete(session.user, ctx.request);
|
|
345
|
+
return ctx.json({
|
|
346
|
+
success: true,
|
|
347
|
+
message: "User deleted"
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
const deleteUserCallback = createAuthEndpoint("/delete-user/callback", {
|
|
351
|
+
method: "GET",
|
|
352
|
+
query: z.object({
|
|
353
|
+
token: z.string().meta({ description: "The token to verify the deletion request" }),
|
|
354
|
+
callbackURL: z.string().meta({ description: "The URL to redirect to after deletion" }).optional()
|
|
355
|
+
}),
|
|
356
|
+
use: [originCheck((ctx) => ctx.query.callbackURL)],
|
|
357
|
+
metadata: { openapi: {
|
|
358
|
+
description: "Callback to complete user deletion with verification token",
|
|
359
|
+
responses: { "200": {
|
|
360
|
+
description: "User successfully deleted",
|
|
361
|
+
content: { "application/json": { schema: {
|
|
362
|
+
type: "object",
|
|
363
|
+
properties: {
|
|
364
|
+
success: {
|
|
365
|
+
type: "boolean",
|
|
366
|
+
description: "Indicates if the deletion was successful"
|
|
367
|
+
},
|
|
368
|
+
message: {
|
|
369
|
+
type: "string",
|
|
370
|
+
enum: ["User deleted"],
|
|
371
|
+
description: "Confirmation message"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
required: ["success", "message"]
|
|
375
|
+
} } }
|
|
376
|
+
} }
|
|
377
|
+
} }
|
|
378
|
+
}, async (ctx) => {
|
|
379
|
+
if (!ctx.context.options.user?.deleteUser?.enabled) {
|
|
380
|
+
ctx.context.logger.error("Delete user is disabled. Enable it in the options");
|
|
381
|
+
throw APIError.from("NOT_FOUND", {
|
|
382
|
+
message: "Not found",
|
|
383
|
+
code: "NOT_FOUND"
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
const session = await getSessionFromCtx(ctx, { disableCookieCache: isStateful(ctx) });
|
|
387
|
+
if (!session) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO);
|
|
388
|
+
const token = await ctx.context.internalAdapter.consumeVerificationValue(`delete-account-${ctx.query.token}`);
|
|
389
|
+
if (!token || token.value !== session.user.id) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.INVALID_TOKEN);
|
|
390
|
+
const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete;
|
|
391
|
+
if (beforeDelete) await beforeDelete(session.user, ctx.request);
|
|
392
|
+
await ctx.context.internalAdapter.deleteUser(session.user.id);
|
|
393
|
+
await ctx.context.internalAdapter.deleteUserSessions(session.user.id);
|
|
394
|
+
await ctx.context.internalAdapter.deleteAccounts(session.user.id);
|
|
395
|
+
deleteSessionCookie(ctx);
|
|
396
|
+
const afterDelete = ctx.context.options.user.deleteUser?.afterDelete;
|
|
397
|
+
if (afterDelete) await afterDelete(session.user, ctx.request);
|
|
398
|
+
if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL || "/");
|
|
399
|
+
return ctx.json({
|
|
400
|
+
success: true,
|
|
401
|
+
message: "User deleted"
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
const changeEmail = createAuthEndpoint("/change-email", {
|
|
405
|
+
method: "POST",
|
|
406
|
+
body: z.object({
|
|
407
|
+
newEmail: z.email().meta({ description: "The new email address to set must be a valid email address" }),
|
|
408
|
+
callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional()
|
|
409
|
+
}),
|
|
410
|
+
use: [sensitiveSessionMiddleware],
|
|
411
|
+
metadata: { openapi: {
|
|
412
|
+
operationId: "changeEmail",
|
|
413
|
+
responses: { "200": {
|
|
414
|
+
description: "Email change request processed successfully",
|
|
415
|
+
content: { "application/json": { schema: {
|
|
416
|
+
type: "object",
|
|
417
|
+
properties: {
|
|
418
|
+
user: {
|
|
419
|
+
type: "object",
|
|
420
|
+
$ref: "#/components/schemas/User"
|
|
421
|
+
},
|
|
422
|
+
status: {
|
|
423
|
+
type: "boolean",
|
|
424
|
+
description: "Indicates if the request was successful"
|
|
425
|
+
},
|
|
426
|
+
message: {
|
|
427
|
+
type: "string",
|
|
428
|
+
enum: ["Email updated", "Verification email sent"],
|
|
429
|
+
description: "Status message of the email change process",
|
|
430
|
+
nullable: true
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
required: ["status"]
|
|
434
|
+
} } }
|
|
435
|
+
} }
|
|
436
|
+
} }
|
|
437
|
+
}, async (ctx) => {
|
|
438
|
+
if (!ctx.context.options.user?.changeEmail?.enabled) {
|
|
439
|
+
ctx.context.logger.error("Change email is disabled.");
|
|
440
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CHANGE_EMAIL_DISABLED);
|
|
441
|
+
}
|
|
442
|
+
const newEmail = ctx.body.newEmail.toLowerCase();
|
|
443
|
+
if (newEmail === ctx.context.session.user.email) {
|
|
444
|
+
ctx.context.logger.warn("Email is the same");
|
|
445
|
+
throw APIError.fromStatus("BAD_REQUEST", { message: "Email is the same" });
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Early config check: ensure at least one email-change flow is
|
|
449
|
+
* available for the current session state. Without this, an
|
|
450
|
+
* existing-email lookup would return 200 while a non-existing
|
|
451
|
+
* email would later throw 400, leaking email existence.
|
|
452
|
+
*/
|
|
453
|
+
const canUpdateWithoutVerification = ctx.context.session.user.emailVerified !== true && ctx.context.options.user.changeEmail.updateEmailWithoutVerification;
|
|
454
|
+
const canSendVerification = ctx.context.options.emailVerification?.sendVerificationEmail;
|
|
455
|
+
const canSendConfirmation = canSendVerification && ctx.context.session.user.emailVerified && ctx.context.options.user.changeEmail.sendChangeEmailConfirmation;
|
|
456
|
+
if (!canUpdateWithoutVerification && !canSendConfirmation && !canSendVerification) {
|
|
457
|
+
ctx.context.logger.error("Verification email isn't enabled.");
|
|
458
|
+
throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" });
|
|
459
|
+
}
|
|
460
|
+
if (await ctx.context.internalAdapter.findUserByEmail(newEmail)) {
|
|
461
|
+
await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn);
|
|
462
|
+
ctx.context.logger.info("Change email attempt for existing email");
|
|
463
|
+
return ctx.json({ status: true });
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* If the email is not verified, we can update the email if the option is enabled
|
|
467
|
+
*/
|
|
468
|
+
if (canUpdateWithoutVerification) {
|
|
469
|
+
await ctx.context.internalAdapter.updateUser(ctx.context.session.user.id, { email: newEmail });
|
|
470
|
+
await setSessionCookie(ctx, {
|
|
471
|
+
session: ctx.context.session.session,
|
|
472
|
+
user: {
|
|
473
|
+
...ctx.context.session.user,
|
|
474
|
+
email: newEmail
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
if (canSendVerification) {
|
|
478
|
+
const token = await createEmailVerificationToken(ctx.context.secret, newEmail, void 0, ctx.context.options.emailVerification?.expiresIn);
|
|
479
|
+
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
|
|
480
|
+
await ctx.context.runInBackgroundOrAwait(canSendVerification({
|
|
481
|
+
user: {
|
|
482
|
+
...ctx.context.session.user,
|
|
483
|
+
email: newEmail
|
|
484
|
+
},
|
|
485
|
+
url,
|
|
486
|
+
token
|
|
487
|
+
}, ctx.request));
|
|
488
|
+
}
|
|
489
|
+
return ctx.json({ status: true });
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* If the email is verified, we need to send a verification email
|
|
493
|
+
*/
|
|
494
|
+
if (canSendConfirmation) {
|
|
495
|
+
const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-confirmation" });
|
|
496
|
+
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
|
|
497
|
+
await ctx.context.runInBackgroundOrAwait(canSendConfirmation({
|
|
498
|
+
user: ctx.context.session.user,
|
|
499
|
+
newEmail,
|
|
500
|
+
url,
|
|
501
|
+
token
|
|
502
|
+
}, ctx.request));
|
|
503
|
+
return ctx.json({ status: true });
|
|
504
|
+
}
|
|
505
|
+
if (!canSendVerification) {
|
|
506
|
+
ctx.context.logger.error("Verification email isn't enabled.");
|
|
507
|
+
throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" });
|
|
508
|
+
}
|
|
509
|
+
const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" });
|
|
510
|
+
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
|
|
511
|
+
await ctx.context.runInBackgroundOrAwait(canSendVerification({
|
|
512
|
+
user: {
|
|
513
|
+
...ctx.context.session.user,
|
|
514
|
+
email: newEmail
|
|
515
|
+
},
|
|
516
|
+
url,
|
|
517
|
+
token
|
|
518
|
+
}, ctx.request));
|
|
519
|
+
return ctx.json({ status: true });
|
|
520
|
+
});
|
|
521
|
+
//#endregion
|
|
522
|
+
export { changeEmail, changePassword, deleteUser, deleteUserCallback, setPassword, updateUser };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/api/state/oauth.d.ts
|
|
2
|
+
type OAuthState = {
|
|
3
|
+
callbackURL: string;
|
|
4
|
+
codeVerifier: string;
|
|
5
|
+
errorURL?: string;
|
|
6
|
+
newUserURL?: string;
|
|
7
|
+
link?: {
|
|
8
|
+
email: string;
|
|
9
|
+
userId: string;
|
|
10
|
+
};
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
requestSignUp?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Server-controlled values that ride the OAuth state across the provider
|
|
15
|
+
* redirect. Nothing here is reachable from the request body: plugins write it
|
|
16
|
+
* with `addOAuthServerContext` and read it back on the callback. Use this for
|
|
17
|
+
* data the server must trust (identity, flow continuation), unlike the
|
|
18
|
+
* top-level `additionalData` keys below.
|
|
19
|
+
*/
|
|
20
|
+
serverContext?: Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Client-supplied `additionalData` is spread onto the top level. Treat every
|
|
23
|
+
* key here as untrusted input: it originates from the request body.
|
|
24
|
+
*/
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Reads the OAuth state for the current request. During a callback it holds the
|
|
29
|
+
* state parsed from the provider redirect; during sign-in it holds the state
|
|
30
|
+
* just generated.
|
|
31
|
+
*
|
|
32
|
+
* Top-level keys beyond the documented fields are client-supplied
|
|
33
|
+
* (`additionalData`) and must not be trusted. Server-trusted values live under
|
|
34
|
+
* {@link OAuthState.serverContext}.
|
|
35
|
+
*/
|
|
36
|
+
declare const getOAuthState: <T extends Record<string, unknown> = Record<string, unknown>>() => Promise<(OAuthState & T) | null>;
|
|
37
|
+
/**
|
|
38
|
+
* @internal Read accumulated server context to embed during state generation.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Attaches server-trusted data to the current OAuth flow so it survives the
|
|
42
|
+
* provider redirect. Call this from a `before` hook on an OAuth sign-in path
|
|
43
|
+
* (for example `/sign-in/social` or `/sign-in/oauth2`). `generateState` embeds
|
|
44
|
+
* the accumulated values into the state, and they become readable on the
|
|
45
|
+
* callback via `getOAuthState().serverContext`.
|
|
46
|
+
*
|
|
47
|
+
* Unlike the request body's `additionalData`, values set here cannot be spoofed
|
|
48
|
+
* by the client. Multiple callers merge: each call adds its own keys. Values are
|
|
49
|
+
* stored as `unknown`, so narrow their shape when reading them back.
|
|
50
|
+
*/
|
|
51
|
+
declare const addOAuthServerContext: (values: Record<string, unknown>) => Promise<void>;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { addOAuthServerContext, getOAuthState };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineRequestState } from "@better-auth/core/context";
|
|
2
|
+
//#region src/api/state/oauth.ts
|
|
3
|
+
const { get: getRawOAuthState, set: setOAuthState } = defineRequestState(() => null);
|
|
4
|
+
/**
|
|
5
|
+
* Reads the OAuth state for the current request. During a callback it holds the
|
|
6
|
+
* state parsed from the provider redirect; during sign-in it holds the state
|
|
7
|
+
* just generated.
|
|
8
|
+
*
|
|
9
|
+
* Top-level keys beyond the documented fields are client-supplied
|
|
10
|
+
* (`additionalData`) and must not be trusted. Server-trusted values live under
|
|
11
|
+
* {@link OAuthState.serverContext}.
|
|
12
|
+
*/
|
|
13
|
+
const getOAuthState = async () => {
|
|
14
|
+
return await getRawOAuthState();
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* @internal Read accumulated server context to embed during state generation.
|
|
18
|
+
*/
|
|
19
|
+
const { get: getOAuthServerContext, set: setOAuthServerContext } = defineRequestState(() => null);
|
|
20
|
+
/**
|
|
21
|
+
* Attaches server-trusted data to the current OAuth flow so it survives the
|
|
22
|
+
* provider redirect. Call this from a `before` hook on an OAuth sign-in path
|
|
23
|
+
* (for example `/sign-in/social` or `/sign-in/oauth2`). `generateState` embeds
|
|
24
|
+
* the accumulated values into the state, and they become readable on the
|
|
25
|
+
* callback via `getOAuthState().serverContext`.
|
|
26
|
+
*
|
|
27
|
+
* Unlike the request body's `additionalData`, values set here cannot be spoofed
|
|
28
|
+
* by the client. Multiple callers merge: each call adds its own keys. Values are
|
|
29
|
+
* stored as `unknown`, so narrow their shape when reading them back.
|
|
30
|
+
*/
|
|
31
|
+
const addOAuthServerContext = async (values) => {
|
|
32
|
+
await setOAuthServerContext({
|
|
33
|
+
...await getOAuthServerContext() ?? {},
|
|
34
|
+
...values
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { addOAuthServerContext, getOAuthServerContext, getOAuthState, setOAuthState };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/api/state/should-session-refresh.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* State for skipping session refresh
|
|
4
|
+
*
|
|
5
|
+
* In some cases, such as when using server-side rendering (SSR) or when dealing with
|
|
6
|
+
* certain types of requests, it may be necessary to skip session refresh to prevent
|
|
7
|
+
* potential inconsistencies between the session data in the database and the session
|
|
8
|
+
* data stored in cookies.
|
|
9
|
+
*/
|
|
10
|
+
declare const getShouldSkipSessionRefresh: () => Promise<boolean | null>, setShouldSkipSessionRefresh: (value: boolean | null) => Promise<void>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { getShouldSkipSessionRefresh, setShouldSkipSessionRefresh };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineRequestState } from "@better-auth/core/context";
|
|
2
|
+
//#region src/api/state/should-session-refresh.ts
|
|
3
|
+
/**
|
|
4
|
+
* State for skipping session refresh
|
|
5
|
+
*
|
|
6
|
+
* In some cases, such as when using server-side rendering (SSR) or when dealing with
|
|
7
|
+
* certain types of requests, it may be necessary to skip session refresh to prevent
|
|
8
|
+
* potential inconsistencies between the session data in the database and the session
|
|
9
|
+
* data stored in cookies.
|
|
10
|
+
*/
|
|
11
|
+
const { get: getShouldSkipSessionRefresh, set: setShouldSkipSessionRefresh } = defineRequestState(() => false);
|
|
12
|
+
//#endregion
|
|
13
|
+
export { getShouldSkipSessionRefresh, setShouldSkipSessionRefresh };
|