@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,619 @@
|
|
|
1
|
+
import { shouldBindAccountCookieToSessionUser } from "../../context/store-capabilities.mjs";
|
|
2
|
+
import { parseAccountOutput } from "../../db/schema.mjs";
|
|
3
|
+
import { getAccountCookie, setAccountCookie } from "../../cookies/session-store.mjs";
|
|
4
|
+
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
5
|
+
import { resolveOAuthAccountKeyForAPI } from "../../oauth2/account-key.mjs";
|
|
6
|
+
import { missingEmailLogMessage } from "../../oauth2/errors.mjs";
|
|
7
|
+
import { decryptOAuthToken, getOAuthCallbackPath, setTokenUtil } from "../../oauth2/utils.mjs";
|
|
8
|
+
import { applyUpdateUserInfoOnLink } from "../../oauth2/link-account.mjs";
|
|
9
|
+
import { generateIdTokenNonce, generateState } from "../../oauth2/state.mjs";
|
|
10
|
+
import { freshSessionMiddleware, getSessionFromCtx, isStateful, sessionMiddleware } from "./session.mjs";
|
|
11
|
+
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
12
|
+
import { additionalAuthorizationParamsSchema, supportsIdTokenSignIn, verifyProviderIdToken } from "@better-auth/core/oauth2";
|
|
13
|
+
import { SocialProviderListEnum } from "@better-auth/core/social-providers";
|
|
14
|
+
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
15
|
+
import * as z from "zod";
|
|
16
|
+
//#region src/api/routes/account.ts
|
|
17
|
+
function parseStoredScopes(scope) {
|
|
18
|
+
if (!scope) return [];
|
|
19
|
+
return scope.split(",").map((s) => s.trim()).filter(Boolean);
|
|
20
|
+
}
|
|
21
|
+
const listUserAccounts = createAuthEndpoint("/list-accounts", {
|
|
22
|
+
method: "GET",
|
|
23
|
+
use: [sessionMiddleware],
|
|
24
|
+
metadata: { openapi: {
|
|
25
|
+
operationId: "listUserAccounts",
|
|
26
|
+
description: "List all accounts linked to the user",
|
|
27
|
+
responses: { "200": {
|
|
28
|
+
description: "Success",
|
|
29
|
+
content: { "application/json": { schema: {
|
|
30
|
+
type: "array",
|
|
31
|
+
items: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
id: { type: "string" },
|
|
35
|
+
providerId: { type: "string" },
|
|
36
|
+
createdAt: {
|
|
37
|
+
type: "string",
|
|
38
|
+
format: "date-time"
|
|
39
|
+
},
|
|
40
|
+
updatedAt: {
|
|
41
|
+
type: "string",
|
|
42
|
+
format: "date-time"
|
|
43
|
+
},
|
|
44
|
+
accountId: { type: "string" },
|
|
45
|
+
userId: { type: "string" },
|
|
46
|
+
scopes: {
|
|
47
|
+
type: "array",
|
|
48
|
+
items: { type: "string" }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
required: [
|
|
52
|
+
"id",
|
|
53
|
+
"providerId",
|
|
54
|
+
"createdAt",
|
|
55
|
+
"updatedAt",
|
|
56
|
+
"accountId",
|
|
57
|
+
"userId",
|
|
58
|
+
"scopes"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
} } }
|
|
62
|
+
} }
|
|
63
|
+
} }
|
|
64
|
+
}, async (c) => {
|
|
65
|
+
const session = c.context.session;
|
|
66
|
+
const accounts = await c.context.internalAdapter.findAccounts(session.user.id);
|
|
67
|
+
return c.json(accounts.map((a) => {
|
|
68
|
+
const { scope, ...parsed } = parseAccountOutput(c.context.options, a);
|
|
69
|
+
return {
|
|
70
|
+
...parsed,
|
|
71
|
+
scopes: parseStoredScopes(scope)
|
|
72
|
+
};
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
const linkSocialAccount = createAuthEndpoint("/link-social", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
requireHeaders: true,
|
|
78
|
+
body: z.object({
|
|
79
|
+
/**
|
|
80
|
+
* Callback URL to redirect to after the user has signed in.
|
|
81
|
+
*/
|
|
82
|
+
callbackURL: z.string().meta({ description: "The URL to redirect to after the user has signed in" }).optional(),
|
|
83
|
+
/**
|
|
84
|
+
* OAuth2 provider to use
|
|
85
|
+
*/
|
|
86
|
+
provider: SocialProviderListEnum,
|
|
87
|
+
/**
|
|
88
|
+
* ID Token for direct authentication without redirect
|
|
89
|
+
*/
|
|
90
|
+
idToken: z.object({
|
|
91
|
+
token: z.string(),
|
|
92
|
+
nonce: z.string().optional(),
|
|
93
|
+
accessToken: z.string().optional(),
|
|
94
|
+
refreshToken: z.string().optional()
|
|
95
|
+
}).optional(),
|
|
96
|
+
/**
|
|
97
|
+
* Whether to allow sign up for new users
|
|
98
|
+
*/
|
|
99
|
+
requestSignUp: z.boolean().optional(),
|
|
100
|
+
/**
|
|
101
|
+
* Additional scopes to request when linking the account.
|
|
102
|
+
* This is useful for requesting additional permissions when
|
|
103
|
+
* linking a social account compared to the initial authentication.
|
|
104
|
+
*/
|
|
105
|
+
scopes: z.array(z.string()).meta({ description: "Additional scopes to request from the provider" }).optional(),
|
|
106
|
+
/**
|
|
107
|
+
* The URL to redirect to if there is an error during the link process.
|
|
108
|
+
*/
|
|
109
|
+
errorCallbackURL: z.string().meta({ description: "The URL to redirect to if there is an error during the link process" }).optional(),
|
|
110
|
+
/**
|
|
111
|
+
* Disable automatic redirection to the provider
|
|
112
|
+
*
|
|
113
|
+
* This is useful if you want to handle the redirection
|
|
114
|
+
* yourself like in a popup or a different tab.
|
|
115
|
+
*/
|
|
116
|
+
disableRedirect: z.boolean().meta({ description: "Disable automatic redirection to the provider. Useful for handling the redirection yourself" }).optional(),
|
|
117
|
+
/**
|
|
118
|
+
* The login hint to forward to the provider authorization endpoint.
|
|
119
|
+
*/
|
|
120
|
+
loginHint: z.string().meta({ description: "The login hint to use for the authorization code request" }).optional(),
|
|
121
|
+
/**
|
|
122
|
+
* Extra query parameters to append to the provider authorization URL.
|
|
123
|
+
* Reserved OAuth keys (state, client_id, redirect_uri, response_type,
|
|
124
|
+
* code_challenge, code_challenge_method, nonce, scope) are rejected.
|
|
125
|
+
*/
|
|
126
|
+
additionalParams: additionalAuthorizationParamsSchema,
|
|
127
|
+
/**
|
|
128
|
+
* Any additional data to pass through the oauth flow.
|
|
129
|
+
*/
|
|
130
|
+
additionalData: z.record(z.string(), z.any()).optional()
|
|
131
|
+
}),
|
|
132
|
+
use: [sessionMiddleware],
|
|
133
|
+
metadata: { openapi: {
|
|
134
|
+
description: "Link a social account to the user",
|
|
135
|
+
operationId: "linkSocialAccount",
|
|
136
|
+
responses: { "200": {
|
|
137
|
+
description: "Success",
|
|
138
|
+
content: { "application/json": { schema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
url: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "The authorization URL to redirect the user to"
|
|
144
|
+
},
|
|
145
|
+
redirect: {
|
|
146
|
+
type: "boolean",
|
|
147
|
+
description: "Indicates if the user should be redirected to the authorization URL"
|
|
148
|
+
},
|
|
149
|
+
status: { type: "boolean" }
|
|
150
|
+
},
|
|
151
|
+
required: ["redirect"]
|
|
152
|
+
} } }
|
|
153
|
+
} }
|
|
154
|
+
} }
|
|
155
|
+
}, async (c) => {
|
|
156
|
+
const session = c.context.session;
|
|
157
|
+
const provider = await getAwaitableValue(c.context.socialProviders, { value: c.body.provider });
|
|
158
|
+
if (!provider) {
|
|
159
|
+
c.context.logger.error("Provider not found. Make sure to add the provider in your auth config", { provider: c.body.provider });
|
|
160
|
+
throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.PROVIDER_NOT_FOUND);
|
|
161
|
+
}
|
|
162
|
+
if (c.body.idToken) {
|
|
163
|
+
if (!supportsIdTokenSignIn(provider)) {
|
|
164
|
+
c.context.logger.error("Provider does not support id token verification", { provider: c.body.provider });
|
|
165
|
+
throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED);
|
|
166
|
+
}
|
|
167
|
+
const { token, nonce } = c.body.idToken;
|
|
168
|
+
if (!await verifyProviderIdToken(provider, token, nonce, c)) {
|
|
169
|
+
c.context.logger.warn("Invalid id token", { provider: c.body.provider });
|
|
170
|
+
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_TOKEN);
|
|
171
|
+
}
|
|
172
|
+
const linkingUserInfo = await provider.getUserInfo({
|
|
173
|
+
idToken: token,
|
|
174
|
+
accessToken: c.body.idToken.accessToken,
|
|
175
|
+
refreshToken: c.body.idToken.refreshToken
|
|
176
|
+
});
|
|
177
|
+
if (!linkingUserInfo || !linkingUserInfo?.user) {
|
|
178
|
+
c.context.logger.error("Failed to get user info", { provider: c.body.provider });
|
|
179
|
+
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO);
|
|
180
|
+
}
|
|
181
|
+
if (!linkingUserInfo.user.email) {
|
|
182
|
+
c.context.logger.error(missingEmailLogMessage(c.body.provider, { source: "id_token" }), { provider: c.body.provider });
|
|
183
|
+
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND);
|
|
184
|
+
}
|
|
185
|
+
const accountKey = await resolveOAuthAccountKeyForAPI(provider, {
|
|
186
|
+
idToken: token,
|
|
187
|
+
accessToken: c.body.idToken.accessToken,
|
|
188
|
+
refreshToken: c.body.idToken.refreshToken
|
|
189
|
+
}, linkingUserInfo.data);
|
|
190
|
+
const linkedAccount = await c.context.internalAdapter.findAccountByKey(accountKey);
|
|
191
|
+
if (linkedAccount?.userId === session.user.id) {
|
|
192
|
+
const updateData = Object.fromEntries(Object.entries({
|
|
193
|
+
providerId: provider.id,
|
|
194
|
+
accessToken: await setTokenUtil(c.body.idToken.accessToken, c.context),
|
|
195
|
+
idToken: token,
|
|
196
|
+
refreshToken: await setTokenUtil(c.body.idToken.refreshToken, c.context)
|
|
197
|
+
}).filter(([_, value]) => value !== void 0));
|
|
198
|
+
await c.context.internalAdapter.updateAccount(linkedAccount.id, updateData);
|
|
199
|
+
await applyUpdateUserInfoOnLink(c, session.user.id, linkingUserInfo.user);
|
|
200
|
+
return c.json({
|
|
201
|
+
url: "",
|
|
202
|
+
status: true,
|
|
203
|
+
redirect: false
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (linkedAccount) throw APIError.from("CONFLICT", BASE_ERROR_CODES.SOCIAL_ACCOUNT_ALREADY_LINKED);
|
|
207
|
+
if (!c.context.trustedProviders.includes(provider.id) && !linkingUserInfo.user.emailVerified || c.context.options.account?.accountLinking?.enabled === false) throw APIError.from("UNAUTHORIZED", {
|
|
208
|
+
message: "Account not linked - linking not allowed",
|
|
209
|
+
code: "LINKING_NOT_ALLOWED"
|
|
210
|
+
});
|
|
211
|
+
if (linkingUserInfo.user.email?.toLowerCase() !== session.user.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) throw APIError.from("UNAUTHORIZED", {
|
|
212
|
+
message: "Account not linked - different emails not allowed",
|
|
213
|
+
code: "LINKING_DIFFERENT_EMAILS_NOT_ALLOWED"
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
await c.context.internalAdapter.createAccount({
|
|
217
|
+
userId: session.user.id,
|
|
218
|
+
...accountKey,
|
|
219
|
+
accessToken: await setTokenUtil(c.body.idToken.accessToken, c.context),
|
|
220
|
+
idToken: token,
|
|
221
|
+
refreshToken: await setTokenUtil(c.body.idToken.refreshToken, c.context)
|
|
222
|
+
});
|
|
223
|
+
} catch {
|
|
224
|
+
throw APIError.from("EXPECTATION_FAILED", {
|
|
225
|
+
message: "Account not linked - unable to create account",
|
|
226
|
+
code: "LINKING_FAILED"
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
await applyUpdateUserInfoOnLink(c, session.user.id, linkingUserInfo.user);
|
|
230
|
+
return c.json({
|
|
231
|
+
url: "",
|
|
232
|
+
status: true,
|
|
233
|
+
redirect: false
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const idTokenNonce = generateIdTokenNonce(provider);
|
|
237
|
+
const state = await generateState(c, {
|
|
238
|
+
link: {
|
|
239
|
+
userId: session.user.id,
|
|
240
|
+
email: session.user.email
|
|
241
|
+
},
|
|
242
|
+
additionalData: c.body.additionalData,
|
|
243
|
+
idTokenNonce
|
|
244
|
+
});
|
|
245
|
+
const url = await provider.createAuthorizationURL({
|
|
246
|
+
state: state.state,
|
|
247
|
+
codeVerifier: state.codeVerifier,
|
|
248
|
+
idTokenNonce,
|
|
249
|
+
redirectURI: `${c.context.baseURL}${getOAuthCallbackPath(provider)}`,
|
|
250
|
+
scopes: c.body.scopes,
|
|
251
|
+
loginHint: c.body.loginHint,
|
|
252
|
+
additionalParams: c.body.additionalParams
|
|
253
|
+
});
|
|
254
|
+
if (!c.body.disableRedirect) c.setHeader("Location", url.toString());
|
|
255
|
+
return c.json({
|
|
256
|
+
url: url.toString(),
|
|
257
|
+
redirect: !c.body.disableRedirect
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
const unlinkAccount = createAuthEndpoint("/unlink-account", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
body: z.object({ accountId: z.string().meta({ description: "The Better Auth account ID to unlink" }) }),
|
|
263
|
+
use: [freshSessionMiddleware],
|
|
264
|
+
metadata: { openapi: {
|
|
265
|
+
description: "Unlink an account",
|
|
266
|
+
responses: { "200": {
|
|
267
|
+
description: "Success",
|
|
268
|
+
content: { "application/json": { schema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: { status: { type: "boolean" } }
|
|
271
|
+
} } }
|
|
272
|
+
} }
|
|
273
|
+
} }
|
|
274
|
+
}, async (ctx) => {
|
|
275
|
+
const { accountId } = ctx.body;
|
|
276
|
+
const accounts = await ctx.context.internalAdapter.findAccounts(ctx.context.session.user.id);
|
|
277
|
+
if (accounts.length === 1 && !ctx.context.options.account?.accountLinking?.allowUnlinkingAll) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.FAILED_TO_UNLINK_LAST_ACCOUNT);
|
|
278
|
+
const accountExist = accounts.find((account) => account.id === accountId);
|
|
279
|
+
if (!accountExist) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
280
|
+
await ctx.context.internalAdapter.deleteAccount(accountExist.id);
|
|
281
|
+
return ctx.json({ status: true });
|
|
282
|
+
});
|
|
283
|
+
/**
|
|
284
|
+
* Resolves the user id an account-token operation should act on.
|
|
285
|
+
*
|
|
286
|
+
* A caller reaching the server over HTTP (a request or session headers are
|
|
287
|
+
* present) must have a valid session, and that session's user always wins.
|
|
288
|
+
* A trusted server-side `auth.api` caller with no session may instead name a
|
|
289
|
+
* `userId` directly. Throws `UNAUTHORIZED` when an HTTP caller is
|
|
290
|
+
* unauthenticated, and `USER_ID_OR_SESSION_REQUIRED` when neither a session
|
|
291
|
+
* nor a `userId` is available.
|
|
292
|
+
*
|
|
293
|
+
* When a durable store is authoritative, bypasses the cookie cache: these
|
|
294
|
+
* routes mint or refresh provider access tokens, so a server-side session
|
|
295
|
+
* revocation must take effect immediately rather than waiting for the cached
|
|
296
|
+
* cookie to expire. DB-less deployments keep the session in the cookie itself,
|
|
297
|
+
* so the cache is left in place for them.
|
|
298
|
+
*/
|
|
299
|
+
async function resolveUserId(ctx, userId) {
|
|
300
|
+
const session = await getSessionFromCtx(ctx, { disableCookieCache: isStateful(ctx) });
|
|
301
|
+
if (!session && (ctx.request || ctx.headers)) throw ctx.error("UNAUTHORIZED");
|
|
302
|
+
const resolvedUserId = session?.user?.id || userId;
|
|
303
|
+
if (!resolvedUserId) throw APIError.from("BAD_REQUEST", {
|
|
304
|
+
message: "Either userId or session is required",
|
|
305
|
+
code: "USER_ID_OR_SESSION_REQUIRED"
|
|
306
|
+
});
|
|
307
|
+
return resolvedUserId;
|
|
308
|
+
}
|
|
309
|
+
const accountSelectionSchema = z.union([z.strictObject({
|
|
310
|
+
accountId: z.string().meta({ description: "The Better Auth account ID" }),
|
|
311
|
+
userId: z.string().meta({ description: "The user ID associated with the account" }).optional()
|
|
312
|
+
}), z.strictObject({
|
|
313
|
+
useAccountCookie: z.literal(true).meta({ description: "Select the current OAuth account from its signed cookie" }),
|
|
314
|
+
userId: z.string().meta({ description: "The user ID associated with the account" }).optional()
|
|
315
|
+
})]);
|
|
316
|
+
function matchesAccountSelection(ctx, account, { resolvedUserId, selection }) {
|
|
317
|
+
return (!shouldBindAccountCookieToSessionUser(ctx.context.options) || account.userId === resolvedUserId) && ("accountId" in selection ? account.id === selection.accountId : true);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Resolves an account from exactly one explicit source.
|
|
321
|
+
*
|
|
322
|
+
* A local account ID is resolved from the database. A signed account cookie is
|
|
323
|
+
* used only when the caller explicitly selects it, which keeps stateless OAuth
|
|
324
|
+
* flows usable without letting cached cookie data satisfy a row-ID lookup.
|
|
325
|
+
*/
|
|
326
|
+
async function resolveUserAccount(ctx, { resolvedUserId, selection }) {
|
|
327
|
+
if ("accountId" in selection) {
|
|
328
|
+
const account = (await ctx.context.internalAdapter.findAccounts(resolvedUserId)).find((candidate) => candidate.id === selection.accountId);
|
|
329
|
+
if (account) return {
|
|
330
|
+
account,
|
|
331
|
+
accountCookie: null
|
|
332
|
+
};
|
|
333
|
+
} else if (ctx.context.options.account?.storeAccountCookie) {
|
|
334
|
+
const accountCookie = await getAccountCookie(ctx);
|
|
335
|
+
if (accountCookie && matchesAccountSelection(ctx, accountCookie, {
|
|
336
|
+
resolvedUserId,
|
|
337
|
+
selection
|
|
338
|
+
})) return {
|
|
339
|
+
account: accountCookie,
|
|
340
|
+
accountCookie
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Fetches a currently-valid access token for a user's provider account,
|
|
347
|
+
* refreshing and persisting it when it is within five seconds of expiry.
|
|
348
|
+
* Shared by the `/get-access-token` endpoint and `/account-info` so both
|
|
349
|
+
* resolve and refresh tokens through one path.
|
|
350
|
+
*/
|
|
351
|
+
async function getValidAccessToken(ctx, { resolvedUserId, selection, account: resolvedAccount }) {
|
|
352
|
+
const account = resolvedAccount ?? (await resolveUserAccount(ctx, {
|
|
353
|
+
resolvedUserId,
|
|
354
|
+
selection
|
|
355
|
+
})).account;
|
|
356
|
+
if (!matchesAccountSelection(ctx, account, {
|
|
357
|
+
resolvedUserId,
|
|
358
|
+
selection
|
|
359
|
+
})) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.ACCOUNT_NOT_FOUND);
|
|
360
|
+
const provider = await getAwaitableValue(ctx.context.socialProviders, { value: account.providerId });
|
|
361
|
+
if (!provider) throw APIError.from("BAD_REQUEST", {
|
|
362
|
+
message: `Provider ${account.providerId} is not supported.`,
|
|
363
|
+
code: "PROVIDER_NOT_SUPPORTED"
|
|
364
|
+
});
|
|
365
|
+
try {
|
|
366
|
+
let newTokens = null;
|
|
367
|
+
const accessTokenExpired = account.accessTokenExpiresAt && new Date(account.accessTokenExpiresAt).getTime() - Date.now() < 5e3;
|
|
368
|
+
if (account.refreshToken && accessTokenExpired && provider.refreshAccessToken) {
|
|
369
|
+
const refreshToken = await decryptOAuthToken(account.refreshToken, ctx.context);
|
|
370
|
+
newTokens = await provider.refreshAccessToken(refreshToken, ctx);
|
|
371
|
+
const updatedData = {
|
|
372
|
+
accessToken: await setTokenUtil(newTokens?.accessToken, ctx.context),
|
|
373
|
+
accessTokenExpiresAt: newTokens?.accessTokenExpiresAt,
|
|
374
|
+
refreshToken: newTokens?.refreshToken ? await setTokenUtil(newTokens.refreshToken, ctx.context) : account.refreshToken,
|
|
375
|
+
refreshTokenExpiresAt: newTokens?.refreshTokenExpiresAt ?? account.refreshTokenExpiresAt,
|
|
376
|
+
idToken: newTokens?.idToken || account.idToken
|
|
377
|
+
};
|
|
378
|
+
let updatedAccount = null;
|
|
379
|
+
if (account.id) updatedAccount = await ctx.context.internalAdapter.updateAccount(account.id, updatedData);
|
|
380
|
+
if (ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, {
|
|
381
|
+
...account,
|
|
382
|
+
...updatedAccount ?? updatedData
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
const accessTokenExpiresAt = (() => {
|
|
386
|
+
if (newTokens?.accessTokenExpiresAt) {
|
|
387
|
+
if (typeof newTokens.accessTokenExpiresAt === "string") return new Date(newTokens.accessTokenExpiresAt);
|
|
388
|
+
return newTokens.accessTokenExpiresAt;
|
|
389
|
+
}
|
|
390
|
+
if (account.accessTokenExpiresAt) {
|
|
391
|
+
if (typeof account.accessTokenExpiresAt === "string") return new Date(account.accessTokenExpiresAt);
|
|
392
|
+
return account.accessTokenExpiresAt;
|
|
393
|
+
}
|
|
394
|
+
})();
|
|
395
|
+
return {
|
|
396
|
+
accessToken: newTokens?.accessToken ?? await decryptOAuthToken(account.accessToken ?? "", ctx.context),
|
|
397
|
+
accessTokenExpiresAt,
|
|
398
|
+
scopes: parseStoredScopes(account.scope),
|
|
399
|
+
idToken: newTokens?.idToken ?? account.idToken ?? void 0
|
|
400
|
+
};
|
|
401
|
+
} catch (_error) {
|
|
402
|
+
throw APIError.from("BAD_REQUEST", {
|
|
403
|
+
message: "Failed to get a valid access token",
|
|
404
|
+
code: "FAILED_TO_GET_ACCESS_TOKEN"
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const getAccessToken = createAuthEndpoint("/get-access-token", {
|
|
409
|
+
method: "POST",
|
|
410
|
+
body: accountSelectionSchema,
|
|
411
|
+
metadata: { openapi: {
|
|
412
|
+
description: "Get a valid access token, doing a refresh if needed",
|
|
413
|
+
responses: {
|
|
414
|
+
200: {
|
|
415
|
+
description: "A Valid access token",
|
|
416
|
+
content: { "application/json": { schema: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
tokenType: { type: "string" },
|
|
420
|
+
idToken: { type: "string" },
|
|
421
|
+
accessToken: { type: "string" },
|
|
422
|
+
accessTokenExpiresAt: {
|
|
423
|
+
type: "string",
|
|
424
|
+
format: "date-time"
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} } }
|
|
428
|
+
},
|
|
429
|
+
400: { description: "Invalid refresh token or provider configuration" }
|
|
430
|
+
}
|
|
431
|
+
} }
|
|
432
|
+
}, async (ctx) => {
|
|
433
|
+
const { userId } = ctx.body;
|
|
434
|
+
const tokens = await getValidAccessToken(ctx, {
|
|
435
|
+
resolvedUserId: await resolveUserId(ctx, userId),
|
|
436
|
+
selection: ctx.body
|
|
437
|
+
});
|
|
438
|
+
return ctx.json(tokens);
|
|
439
|
+
});
|
|
440
|
+
const refreshToken = createAuthEndpoint("/refresh-token", {
|
|
441
|
+
method: "POST",
|
|
442
|
+
body: accountSelectionSchema,
|
|
443
|
+
metadata: { openapi: {
|
|
444
|
+
description: "Refresh the access token using a refresh token",
|
|
445
|
+
responses: {
|
|
446
|
+
200: {
|
|
447
|
+
description: "Access token refreshed successfully",
|
|
448
|
+
content: { "application/json": { schema: {
|
|
449
|
+
type: "object",
|
|
450
|
+
properties: {
|
|
451
|
+
tokenType: { type: "string" },
|
|
452
|
+
idToken: { type: "string" },
|
|
453
|
+
accessToken: { type: "string" },
|
|
454
|
+
refreshToken: { type: "string" },
|
|
455
|
+
accessTokenExpiresAt: {
|
|
456
|
+
type: "string",
|
|
457
|
+
format: "date-time"
|
|
458
|
+
},
|
|
459
|
+
refreshTokenExpiresAt: {
|
|
460
|
+
type: "string",
|
|
461
|
+
format: "date-time"
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} } }
|
|
465
|
+
},
|
|
466
|
+
400: { description: "Invalid refresh token or provider configuration" }
|
|
467
|
+
}
|
|
468
|
+
} }
|
|
469
|
+
}, async (ctx) => {
|
|
470
|
+
const { userId } = ctx.body;
|
|
471
|
+
const { account, accountCookie } = await resolveUserAccount(ctx, {
|
|
472
|
+
resolvedUserId: await resolveUserId(ctx, userId),
|
|
473
|
+
selection: ctx.body
|
|
474
|
+
});
|
|
475
|
+
const provider = await getAwaitableValue(ctx.context.socialProviders, { value: account.providerId });
|
|
476
|
+
if (!provider) throw APIError.from("BAD_REQUEST", {
|
|
477
|
+
message: `Provider ${account.providerId} is not supported.`,
|
|
478
|
+
code: "PROVIDER_NOT_SUPPORTED"
|
|
479
|
+
});
|
|
480
|
+
if (!provider.refreshAccessToken) throw APIError.from("BAD_REQUEST", {
|
|
481
|
+
message: `Provider ${account.providerId} does not support token refreshing.`,
|
|
482
|
+
code: "TOKEN_REFRESH_NOT_SUPPORTED"
|
|
483
|
+
});
|
|
484
|
+
const refreshToken = account.refreshToken ?? void 0;
|
|
485
|
+
if (!refreshToken) throw APIError.from("BAD_REQUEST", {
|
|
486
|
+
message: "Refresh token not found",
|
|
487
|
+
code: "REFRESH_TOKEN_NOT_FOUND"
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
const decryptedRefreshToken = await decryptOAuthToken(refreshToken, ctx.context);
|
|
491
|
+
const tokens = await provider.refreshAccessToken(decryptedRefreshToken, ctx);
|
|
492
|
+
const resolvedRefreshToken = tokens.refreshToken ? await setTokenUtil(tokens.refreshToken, ctx.context) : refreshToken;
|
|
493
|
+
const resolvedRefreshTokenExpiresAt = tokens.refreshTokenExpiresAt ?? account.refreshTokenExpiresAt;
|
|
494
|
+
const updatedTokenData = {
|
|
495
|
+
accessToken: await setTokenUtil(tokens.accessToken, ctx.context),
|
|
496
|
+
refreshToken: resolvedRefreshToken,
|
|
497
|
+
accessTokenExpiresAt: tokens.accessTokenExpiresAt,
|
|
498
|
+
refreshTokenExpiresAt: resolvedRefreshTokenExpiresAt,
|
|
499
|
+
idToken: tokens.idToken || account.idToken
|
|
500
|
+
};
|
|
501
|
+
let updatedAccount = null;
|
|
502
|
+
if (account.id)
|
|
503
|
+
/**
|
|
504
|
+
* `scope` intentionally omitted. Refresh response may be narrower.
|
|
505
|
+
*
|
|
506
|
+
* @see {@link Account.scope}
|
|
507
|
+
*/
|
|
508
|
+
updatedAccount = await ctx.context.internalAdapter.updateAccount(account.id, updatedTokenData);
|
|
509
|
+
if (accountCookie?.id === account.id && ctx.context.options.account?.storeAccountCookie) await setAccountCookie(ctx, {
|
|
510
|
+
...accountCookie,
|
|
511
|
+
...updatedAccount ?? updatedTokenData
|
|
512
|
+
});
|
|
513
|
+
const responseScope = updatedAccount?.scope ?? account.scope;
|
|
514
|
+
return ctx.json({
|
|
515
|
+
accessToken: tokens.accessToken,
|
|
516
|
+
refreshToken: tokens.refreshToken ?? decryptedRefreshToken,
|
|
517
|
+
accessTokenExpiresAt: tokens.accessTokenExpiresAt,
|
|
518
|
+
refreshTokenExpiresAt: resolvedRefreshTokenExpiresAt,
|
|
519
|
+
scope: responseScope,
|
|
520
|
+
idToken: tokens.idToken || account.idToken,
|
|
521
|
+
providerId: account.providerId,
|
|
522
|
+
accountId: account.id
|
|
523
|
+
});
|
|
524
|
+
} catch (_error) {
|
|
525
|
+
throw APIError.from("BAD_REQUEST", {
|
|
526
|
+
message: "Failed to refresh access token",
|
|
527
|
+
code: "FAILED_TO_REFRESH_ACCESS_TOKEN"
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
const accountInfo = createAuthEndpoint("/account-info", {
|
|
532
|
+
method: "GET",
|
|
533
|
+
metadata: { openapi: {
|
|
534
|
+
description: "Get the account info provided by the provider",
|
|
535
|
+
responses: { "200": {
|
|
536
|
+
description: "Success",
|
|
537
|
+
content: { "application/json": { schema: {
|
|
538
|
+
type: "object",
|
|
539
|
+
properties: {
|
|
540
|
+
user: {
|
|
541
|
+
type: "object",
|
|
542
|
+
properties: {
|
|
543
|
+
name: { type: "string" },
|
|
544
|
+
email: {
|
|
545
|
+
type: "string",
|
|
546
|
+
nullable: true
|
|
547
|
+
},
|
|
548
|
+
image: { type: "string" },
|
|
549
|
+
emailVerified: { type: "boolean" }
|
|
550
|
+
},
|
|
551
|
+
required: ["emailVerified"]
|
|
552
|
+
},
|
|
553
|
+
account: {
|
|
554
|
+
type: "object",
|
|
555
|
+
properties: {
|
|
556
|
+
id: { type: "string" },
|
|
557
|
+
providerId: { type: "string" },
|
|
558
|
+
accountId: { type: "string" }
|
|
559
|
+
},
|
|
560
|
+
required: [
|
|
561
|
+
"id",
|
|
562
|
+
"providerId",
|
|
563
|
+
"accountId"
|
|
564
|
+
],
|
|
565
|
+
additionalProperties: false
|
|
566
|
+
},
|
|
567
|
+
data: {
|
|
568
|
+
type: "object",
|
|
569
|
+
properties: {},
|
|
570
|
+
additionalProperties: true
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
required: [
|
|
574
|
+
"user",
|
|
575
|
+
"data",
|
|
576
|
+
"account"
|
|
577
|
+
],
|
|
578
|
+
additionalProperties: false
|
|
579
|
+
} } }
|
|
580
|
+
} }
|
|
581
|
+
} },
|
|
582
|
+
query: accountSelectionSchema
|
|
583
|
+
}, async (ctx) => {
|
|
584
|
+
const { userId } = ctx.query;
|
|
585
|
+
const resolvedUserId = await resolveUserId(ctx, userId);
|
|
586
|
+
const { account } = await resolveUserAccount(ctx, {
|
|
587
|
+
resolvedUserId,
|
|
588
|
+
selection: ctx.query
|
|
589
|
+
});
|
|
590
|
+
const provider = await getAwaitableValue(ctx.context.socialProviders, { value: account.providerId });
|
|
591
|
+
if (!provider) throw APIError.from("BAD_REQUEST", {
|
|
592
|
+
message: "Account is not associated with a configured social provider.",
|
|
593
|
+
code: "PROVIDER_NOT_CONFIGURED"
|
|
594
|
+
});
|
|
595
|
+
const tokens = await getValidAccessToken(ctx, {
|
|
596
|
+
resolvedUserId,
|
|
597
|
+
selection: ctx.query,
|
|
598
|
+
account
|
|
599
|
+
});
|
|
600
|
+
if (!tokens.accessToken) throw APIError.from("BAD_REQUEST", {
|
|
601
|
+
message: "Access token not found",
|
|
602
|
+
code: "ACCESS_TOKEN_NOT_FOUND"
|
|
603
|
+
});
|
|
604
|
+
const info = await provider.getUserInfo({
|
|
605
|
+
...tokens,
|
|
606
|
+
accessToken: tokens.accessToken
|
|
607
|
+
});
|
|
608
|
+
if (!info) throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO);
|
|
609
|
+
return ctx.json({
|
|
610
|
+
...info,
|
|
611
|
+
account: {
|
|
612
|
+
id: account.id,
|
|
613
|
+
providerId: account.providerId,
|
|
614
|
+
accountId: account.accountId
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
//#endregion
|
|
619
|
+
export { accountInfo, getAccessToken, linkSocialAccount, listUserAccounts, refreshToken, unlinkAccount };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as _$better_call0 from "better-call";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/api/routes/callback.d.ts
|
|
5
|
+
declare const callbackOAuth: _$better_call0.StrictEndpoint<"/callback/:id", {
|
|
6
|
+
method: ("GET" | "POST")[];
|
|
7
|
+
operationId: string;
|
|
8
|
+
body: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
code: z.ZodOptional<z.ZodString>;
|
|
10
|
+
error: z.ZodOptional<z.ZodString>;
|
|
11
|
+
device_id: z.ZodOptional<z.ZodString>;
|
|
12
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
13
|
+
state: z.ZodOptional<z.ZodString>;
|
|
14
|
+
user: z.ZodOptional<z.ZodString>;
|
|
15
|
+
iss: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>>;
|
|
17
|
+
query: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
code: z.ZodOptional<z.ZodString>;
|
|
19
|
+
error: z.ZodOptional<z.ZodString>;
|
|
20
|
+
device_id: z.ZodOptional<z.ZodString>;
|
|
21
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
22
|
+
state: z.ZodOptional<z.ZodString>;
|
|
23
|
+
user: z.ZodOptional<z.ZodString>;
|
|
24
|
+
iss: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>>;
|
|
26
|
+
metadata: {
|
|
27
|
+
allowedMediaTypes: string[];
|
|
28
|
+
scope: "server";
|
|
29
|
+
};
|
|
30
|
+
}, void>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { callbackOAuth };
|