@hammadj/better-auth 1.5.0-beta.10
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/README.md +33 -0
- package/dist/_virtual/rolldown_runtime.mjs +36 -0
- package/dist/adapters/drizzle-adapter/index.d.mts +1 -0
- package/dist/adapters/drizzle-adapter/index.mjs +3 -0
- package/dist/adapters/index.d.mts +23 -0
- package/dist/adapters/index.mjs +13 -0
- package/dist/adapters/index.mjs.map +1 -0
- package/dist/adapters/kysely-adapter/index.d.mts +1 -0
- package/dist/adapters/kysely-adapter/index.mjs +3 -0
- package/dist/adapters/memory-adapter/index.d.mts +1 -0
- package/dist/adapters/memory-adapter/index.mjs +3 -0
- package/dist/adapters/mongodb-adapter/index.d.mts +1 -0
- package/dist/adapters/mongodb-adapter/index.mjs +3 -0
- package/dist/adapters/prisma-adapter/index.d.mts +1 -0
- package/dist/adapters/prisma-adapter/index.mjs +3 -0
- package/dist/api/index.d.mts +40 -0
- package/dist/api/index.mjs +205 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/api/middlewares/index.d.mts +1 -0
- package/dist/api/middlewares/index.mjs +3 -0
- package/dist/api/middlewares/origin-check.d.mts +17 -0
- package/dist/api/middlewares/origin-check.mjs +140 -0
- package/dist/api/middlewares/origin-check.mjs.map +1 -0
- package/dist/api/rate-limiter/index.mjs +177 -0
- package/dist/api/rate-limiter/index.mjs.map +1 -0
- package/dist/api/routes/account.d.mts +10 -0
- package/dist/api/routes/account.mjs +493 -0
- package/dist/api/routes/account.mjs.map +1 -0
- package/dist/api/routes/callback.d.mts +5 -0
- package/dist/api/routes/callback.mjs +178 -0
- package/dist/api/routes/callback.mjs.map +1 -0
- package/dist/api/routes/email-verification.d.mts +29 -0
- package/dist/api/routes/email-verification.mjs +301 -0
- package/dist/api/routes/email-verification.mjs.map +1 -0
- package/dist/api/routes/error.d.mts +5 -0
- package/dist/api/routes/error.mjs +386 -0
- package/dist/api/routes/error.mjs.map +1 -0
- package/dist/api/routes/index.d.mts +11 -0
- package/dist/api/routes/index.mjs +13 -0
- package/dist/api/routes/ok.d.mts +5 -0
- package/dist/api/routes/ok.mjs +30 -0
- package/dist/api/routes/ok.mjs.map +1 -0
- package/dist/api/routes/password.d.mts +8 -0
- package/dist/api/routes/password.mjs +198 -0
- package/dist/api/routes/password.mjs.map +1 -0
- package/dist/api/routes/session.d.mts +52 -0
- package/dist/api/routes/session.mjs +478 -0
- package/dist/api/routes/session.mjs.map +1 -0
- package/dist/api/routes/sign-in.d.mts +8 -0
- package/dist/api/routes/sign-in.mjs +262 -0
- package/dist/api/routes/sign-in.mjs.map +1 -0
- package/dist/api/routes/sign-out.d.mts +5 -0
- package/dist/api/routes/sign-out.mjs +33 -0
- package/dist/api/routes/sign-out.mjs.map +1 -0
- package/dist/api/routes/sign-up.d.mts +7 -0
- package/dist/api/routes/sign-up.mjs +227 -0
- package/dist/api/routes/sign-up.mjs.map +1 -0
- package/dist/api/routes/update-user.d.mts +12 -0
- package/dist/api/routes/update-user.mjs +493 -0
- package/dist/api/routes/update-user.mjs.map +1 -0
- package/dist/api/state/oauth.d.mts +5 -0
- package/dist/api/state/oauth.mjs +8 -0
- package/dist/api/state/oauth.mjs.map +1 -0
- package/dist/api/state/should-session-refresh.d.mts +13 -0
- package/dist/api/state/should-session-refresh.mjs +16 -0
- package/dist/api/state/should-session-refresh.mjs.map +1 -0
- package/dist/api/to-auth-endpoints.mjs +197 -0
- package/dist/api/to-auth-endpoints.mjs.map +1 -0
- package/dist/auth/base.mjs +44 -0
- package/dist/auth/base.mjs.map +1 -0
- package/dist/auth/full.d.mts +30 -0
- package/dist/auth/full.mjs +32 -0
- package/dist/auth/full.mjs.map +1 -0
- package/dist/auth/minimal.d.mts +12 -0
- package/dist/auth/minimal.mjs +14 -0
- package/dist/auth/minimal.mjs.map +1 -0
- package/dist/auth/trusted-origins.mjs +31 -0
- package/dist/auth/trusted-origins.mjs.map +1 -0
- package/dist/client/broadcast-channel.d.mts +20 -0
- package/dist/client/broadcast-channel.mjs +46 -0
- package/dist/client/broadcast-channel.mjs.map +1 -0
- package/dist/client/config.mjs +90 -0
- package/dist/client/config.mjs.map +1 -0
- package/dist/client/fetch-plugins.mjs +18 -0
- package/dist/client/fetch-plugins.mjs.map +1 -0
- package/dist/client/focus-manager.d.mts +11 -0
- package/dist/client/focus-manager.mjs +32 -0
- package/dist/client/focus-manager.mjs.map +1 -0
- package/dist/client/index.d.mts +30 -0
- package/dist/client/index.mjs +21 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/client/lynx/index.d.mts +62 -0
- package/dist/client/lynx/index.mjs +24 -0
- package/dist/client/lynx/index.mjs.map +1 -0
- package/dist/client/lynx/lynx-store.d.mts +47 -0
- package/dist/client/lynx/lynx-store.mjs +47 -0
- package/dist/client/lynx/lynx-store.mjs.map +1 -0
- package/dist/client/online-manager.d.mts +12 -0
- package/dist/client/online-manager.mjs +35 -0
- package/dist/client/online-manager.mjs.map +1 -0
- package/dist/client/parser.mjs +73 -0
- package/dist/client/parser.mjs.map +1 -0
- package/dist/client/path-to-object.d.mts +57 -0
- package/dist/client/plugins/index.d.mts +58 -0
- package/dist/client/plugins/index.mjs +33 -0
- package/dist/client/plugins/infer-plugin.d.mts +9 -0
- package/dist/client/plugins/infer-plugin.mjs +11 -0
- package/dist/client/plugins/infer-plugin.mjs.map +1 -0
- package/dist/client/proxy.mjs +79 -0
- package/dist/client/proxy.mjs.map +1 -0
- package/dist/client/query.d.mts +23 -0
- package/dist/client/query.mjs +98 -0
- package/dist/client/query.mjs.map +1 -0
- package/dist/client/react/index.d.mts +63 -0
- package/dist/client/react/index.mjs +24 -0
- package/dist/client/react/index.mjs.map +1 -0
- package/dist/client/react/react-store.d.mts +47 -0
- package/dist/client/react/react-store.mjs +47 -0
- package/dist/client/react/react-store.mjs.map +1 -0
- package/dist/client/session-atom.mjs +29 -0
- package/dist/client/session-atom.mjs.map +1 -0
- package/dist/client/session-refresh.d.mts +28 -0
- package/dist/client/session-refresh.mjs +140 -0
- package/dist/client/session-refresh.mjs.map +1 -0
- package/dist/client/solid/index.d.mts +57 -0
- package/dist/client/solid/index.mjs +22 -0
- package/dist/client/solid/index.mjs.map +1 -0
- package/dist/client/solid/solid-store.mjs +24 -0
- package/dist/client/solid/solid-store.mjs.map +1 -0
- package/dist/client/svelte/index.d.mts +63 -0
- package/dist/client/svelte/index.mjs +20 -0
- package/dist/client/svelte/index.mjs.map +1 -0
- package/dist/client/types.d.mts +58 -0
- package/dist/client/vanilla.d.mts +62 -0
- package/dist/client/vanilla.mjs +20 -0
- package/dist/client/vanilla.mjs.map +1 -0
- package/dist/client/vue/index.d.mts +86 -0
- package/dist/client/vue/index.mjs +38 -0
- package/dist/client/vue/index.mjs.map +1 -0
- package/dist/client/vue/vue-store.mjs +26 -0
- package/dist/client/vue/vue-store.mjs.map +1 -0
- package/dist/context/create-context.mjs +211 -0
- package/dist/context/create-context.mjs.map +1 -0
- package/dist/context/helpers.mjs +62 -0
- package/dist/context/helpers.mjs.map +1 -0
- package/dist/context/init-minimal.mjs +20 -0
- package/dist/context/init-minimal.mjs.map +1 -0
- package/dist/context/init.mjs +22 -0
- package/dist/context/init.mjs.map +1 -0
- package/dist/cookies/cookie-utils.d.mts +29 -0
- package/dist/cookies/cookie-utils.mjs +105 -0
- package/dist/cookies/cookie-utils.mjs.map +1 -0
- package/dist/cookies/index.d.mts +67 -0
- package/dist/cookies/index.mjs +264 -0
- package/dist/cookies/index.mjs.map +1 -0
- package/dist/cookies/session-store.d.mts +36 -0
- package/dist/cookies/session-store.mjs +200 -0
- package/dist/cookies/session-store.mjs.map +1 -0
- package/dist/crypto/buffer.d.mts +8 -0
- package/dist/crypto/buffer.mjs +18 -0
- package/dist/crypto/buffer.mjs.map +1 -0
- package/dist/crypto/index.d.mts +27 -0
- package/dist/crypto/index.mjs +38 -0
- package/dist/crypto/index.mjs.map +1 -0
- package/dist/crypto/jwt.d.mts +8 -0
- package/dist/crypto/jwt.mjs +95 -0
- package/dist/crypto/jwt.mjs.map +1 -0
- package/dist/crypto/password.d.mts +12 -0
- package/dist/crypto/password.mjs +36 -0
- package/dist/crypto/password.mjs.map +1 -0
- package/dist/crypto/random.d.mts +5 -0
- package/dist/crypto/random.mjs +8 -0
- package/dist/crypto/random.mjs.map +1 -0
- package/dist/db/adapter-base.d.mts +8 -0
- package/dist/db/adapter-base.mjs +28 -0
- package/dist/db/adapter-base.mjs.map +1 -0
- package/dist/db/adapter-kysely.d.mts +8 -0
- package/dist/db/adapter-kysely.mjs +21 -0
- package/dist/db/adapter-kysely.mjs.map +1 -0
- package/dist/db/field-converter.d.mts +8 -0
- package/dist/db/field-converter.mjs +21 -0
- package/dist/db/field-converter.mjs.map +1 -0
- package/dist/db/field.d.mts +55 -0
- package/dist/db/field.mjs +11 -0
- package/dist/db/field.mjs.map +1 -0
- package/dist/db/get-migration.d.mts +23 -0
- package/dist/db/get-migration.mjs +339 -0
- package/dist/db/get-migration.mjs.map +1 -0
- package/dist/db/get-schema.d.mts +11 -0
- package/dist/db/get-schema.mjs +39 -0
- package/dist/db/get-schema.mjs.map +1 -0
- package/dist/db/index.d.mts +9 -0
- package/dist/db/index.mjs +36 -0
- package/dist/db/index.mjs.map +1 -0
- package/dist/db/internal-adapter.d.mts +14 -0
- package/dist/db/internal-adapter.mjs +616 -0
- package/dist/db/internal-adapter.mjs.map +1 -0
- package/dist/db/schema.d.mts +26 -0
- package/dist/db/schema.mjs +118 -0
- package/dist/db/schema.mjs.map +1 -0
- package/dist/db/to-zod.d.mts +36 -0
- package/dist/db/to-zod.mjs +26 -0
- package/dist/db/to-zod.mjs.map +1 -0
- package/dist/db/verification-token-storage.mjs +28 -0
- package/dist/db/verification-token-storage.mjs.map +1 -0
- package/dist/db/with-hooks.d.mts +33 -0
- package/dist/db/with-hooks.mjs +159 -0
- package/dist/db/with-hooks.mjs.map +1 -0
- package/dist/index.d.mts +52 -0
- package/dist/index.mjs +26 -0
- package/dist/integrations/next-js.d.mts +14 -0
- package/dist/integrations/next-js.mjs +78 -0
- package/dist/integrations/next-js.mjs.map +1 -0
- package/dist/integrations/node.d.mts +13 -0
- package/dist/integrations/node.mjs +16 -0
- package/dist/integrations/node.mjs.map +1 -0
- package/dist/integrations/solid-start.d.mts +23 -0
- package/dist/integrations/solid-start.mjs +17 -0
- package/dist/integrations/solid-start.mjs.map +1 -0
- package/dist/integrations/svelte-kit.d.mts +29 -0
- package/dist/integrations/svelte-kit.mjs +57 -0
- package/dist/integrations/svelte-kit.mjs.map +1 -0
- package/dist/integrations/tanstack-start-solid.d.mts +22 -0
- package/dist/integrations/tanstack-start-solid.mjs +61 -0
- package/dist/integrations/tanstack-start-solid.mjs.map +1 -0
- package/dist/integrations/tanstack-start.d.mts +22 -0
- package/dist/integrations/tanstack-start.mjs +61 -0
- package/dist/integrations/tanstack-start.mjs.map +1 -0
- package/dist/oauth2/index.d.mts +5 -0
- package/dist/oauth2/index.mjs +7 -0
- package/dist/oauth2/link-account.d.mts +31 -0
- package/dist/oauth2/link-account.mjs +144 -0
- package/dist/oauth2/link-account.mjs.map +1 -0
- package/dist/oauth2/state.d.mts +26 -0
- package/dist/oauth2/state.mjs +51 -0
- package/dist/oauth2/state.mjs.map +1 -0
- package/dist/oauth2/utils.d.mts +8 -0
- package/dist/oauth2/utils.mjs +31 -0
- package/dist/oauth2/utils.mjs.map +1 -0
- package/dist/plugins/access/access.d.mts +30 -0
- package/dist/plugins/access/access.mjs +46 -0
- package/dist/plugins/access/access.mjs.map +1 -0
- package/dist/plugins/access/index.d.mts +3 -0
- package/dist/plugins/access/index.mjs +3 -0
- package/dist/plugins/access/types.d.mts +17 -0
- package/dist/plugins/additional-fields/client.d.mts +14 -0
- package/dist/plugins/additional-fields/client.mjs +11 -0
- package/dist/plugins/additional-fields/client.mjs.map +1 -0
- package/dist/plugins/admin/access/index.d.mts +2 -0
- package/dist/plugins/admin/access/index.mjs +3 -0
- package/dist/plugins/admin/access/statement.d.mts +118 -0
- package/dist/plugins/admin/access/statement.mjs +53 -0
- package/dist/plugins/admin/access/statement.mjs.map +1 -0
- package/dist/plugins/admin/admin.d.mts +14 -0
- package/dist/plugins/admin/admin.mjs +95 -0
- package/dist/plugins/admin/admin.mjs.map +1 -0
- package/dist/plugins/admin/client.d.mts +14 -0
- package/dist/plugins/admin/client.mjs +36 -0
- package/dist/plugins/admin/client.mjs.map +1 -0
- package/dist/plugins/admin/error-codes.d.mts +5 -0
- package/dist/plugins/admin/error-codes.mjs +30 -0
- package/dist/plugins/admin/error-codes.mjs.map +1 -0
- package/dist/plugins/admin/has-permission.mjs +16 -0
- package/dist/plugins/admin/has-permission.mjs.map +1 -0
- package/dist/plugins/admin/index.d.mts +3 -0
- package/dist/plugins/admin/index.mjs +3 -0
- package/dist/plugins/admin/routes.mjs +855 -0
- package/dist/plugins/admin/routes.mjs.map +1 -0
- package/dist/plugins/admin/schema.d.mts +6 -0
- package/dist/plugins/admin/schema.mjs +34 -0
- package/dist/plugins/admin/schema.mjs.map +1 -0
- package/dist/plugins/admin/types.d.mts +89 -0
- package/dist/plugins/anonymous/client.d.mts +9 -0
- package/dist/plugins/anonymous/client.mjs +22 -0
- package/dist/plugins/anonymous/client.mjs.map +1 -0
- package/dist/plugins/anonymous/error-codes.d.mts +5 -0
- package/dist/plugins/anonymous/error-codes.mjs +16 -0
- package/dist/plugins/anonymous/error-codes.mjs.map +1 -0
- package/dist/plugins/anonymous/index.d.mts +14 -0
- package/dist/plugins/anonymous/index.mjs +163 -0
- package/dist/plugins/anonymous/index.mjs.map +1 -0
- package/dist/plugins/anonymous/schema.d.mts +5 -0
- package/dist/plugins/anonymous/schema.mjs +11 -0
- package/dist/plugins/anonymous/schema.mjs.map +1 -0
- package/dist/plugins/anonymous/types.d.mts +68 -0
- package/dist/plugins/api-key/adapter.mjs +468 -0
- package/dist/plugins/api-key/adapter.mjs.map +1 -0
- package/dist/plugins/api-key/client.d.mts +9 -0
- package/dist/plugins/api-key/client.mjs +19 -0
- package/dist/plugins/api-key/client.mjs.map +1 -0
- package/dist/plugins/api-key/error-codes.d.mts +5 -0
- package/dist/plugins/api-key/error-codes.mjs +34 -0
- package/dist/plugins/api-key/error-codes.mjs.map +1 -0
- package/dist/plugins/api-key/index.d.mts +17 -0
- package/dist/plugins/api-key/index.mjs +134 -0
- package/dist/plugins/api-key/index.mjs.map +1 -0
- package/dist/plugins/api-key/rate-limit.mjs +74 -0
- package/dist/plugins/api-key/rate-limit.mjs.map +1 -0
- package/dist/plugins/api-key/routes/create-api-key.mjs +252 -0
- package/dist/plugins/api-key/routes/create-api-key.mjs.map +1 -0
- package/dist/plugins/api-key/routes/delete-all-expired-api-keys.mjs +24 -0
- package/dist/plugins/api-key/routes/delete-all-expired-api-keys.mjs.map +1 -0
- package/dist/plugins/api-key/routes/delete-api-key.mjs +74 -0
- package/dist/plugins/api-key/routes/delete-api-key.mjs.map +1 -0
- package/dist/plugins/api-key/routes/get-api-key.mjs +158 -0
- package/dist/plugins/api-key/routes/get-api-key.mjs.map +1 -0
- package/dist/plugins/api-key/routes/index.mjs +71 -0
- package/dist/plugins/api-key/routes/index.mjs.map +1 -0
- package/dist/plugins/api-key/routes/list-api-keys.mjs +194 -0
- package/dist/plugins/api-key/routes/list-api-keys.mjs.map +1 -0
- package/dist/plugins/api-key/routes/update-api-key.mjs +248 -0
- package/dist/plugins/api-key/routes/update-api-key.mjs.map +1 -0
- package/dist/plugins/api-key/routes/verify-api-key.mjs +223 -0
- package/dist/plugins/api-key/routes/verify-api-key.mjs.map +1 -0
- package/dist/plugins/api-key/schema.d.mts +11 -0
- package/dist/plugins/api-key/schema.mjs +130 -0
- package/dist/plugins/api-key/schema.mjs.map +1 -0
- package/dist/plugins/api-key/types.d.mts +346 -0
- package/dist/plugins/bearer/index.d.mts +25 -0
- package/dist/plugins/bearer/index.mjs +66 -0
- package/dist/plugins/bearer/index.mjs.map +1 -0
- package/dist/plugins/captcha/constants.d.mts +10 -0
- package/dist/plugins/captcha/constants.mjs +22 -0
- package/dist/plugins/captcha/constants.mjs.map +1 -0
- package/dist/plugins/captcha/error-codes.mjs +16 -0
- package/dist/plugins/captcha/error-codes.mjs.map +1 -0
- package/dist/plugins/captcha/index.d.mts +14 -0
- package/dist/plugins/captcha/index.mjs +60 -0
- package/dist/plugins/captcha/index.mjs.map +1 -0
- package/dist/plugins/captcha/types.d.mts +28 -0
- package/dist/plugins/captcha/utils.mjs +11 -0
- package/dist/plugins/captcha/utils.mjs.map +1 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +27 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs.map +1 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +25 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs.map +1 -0
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +29 -0
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs.map +1 -0
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +27 -0
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs.map +1 -0
- package/dist/plugins/captcha/verify-handlers/index.mjs +6 -0
- package/dist/plugins/custom-session/client.d.mts +10 -0
- package/dist/plugins/custom-session/client.mjs +11 -0
- package/dist/plugins/custom-session/client.mjs.map +1 -0
- package/dist/plugins/custom-session/index.d.mts +26 -0
- package/dist/plugins/custom-session/index.mjs +70 -0
- package/dist/plugins/custom-session/index.mjs.map +1 -0
- package/dist/plugins/device-authorization/client.d.mts +5 -0
- package/dist/plugins/device-authorization/client.mjs +18 -0
- package/dist/plugins/device-authorization/client.mjs.map +1 -0
- package/dist/plugins/device-authorization/error-codes.mjs +21 -0
- package/dist/plugins/device-authorization/error-codes.mjs.map +1 -0
- package/dist/plugins/device-authorization/index.d.mts +28 -0
- package/dist/plugins/device-authorization/index.mjs +50 -0
- package/dist/plugins/device-authorization/index.mjs.map +1 -0
- package/dist/plugins/device-authorization/routes.mjs +510 -0
- package/dist/plugins/device-authorization/routes.mjs.map +1 -0
- package/dist/plugins/device-authorization/schema.mjs +57 -0
- package/dist/plugins/device-authorization/schema.mjs.map +1 -0
- package/dist/plugins/email-otp/client.d.mts +7 -0
- package/dist/plugins/email-otp/client.mjs +18 -0
- package/dist/plugins/email-otp/client.mjs.map +1 -0
- package/dist/plugins/email-otp/error-codes.d.mts +5 -0
- package/dist/plugins/email-otp/error-codes.mjs +12 -0
- package/dist/plugins/email-otp/error-codes.mjs.map +1 -0
- package/dist/plugins/email-otp/index.d.mts +14 -0
- package/dist/plugins/email-otp/index.mjs +108 -0
- package/dist/plugins/email-otp/index.mjs.map +1 -0
- package/dist/plugins/email-otp/otp-token.mjs +29 -0
- package/dist/plugins/email-otp/otp-token.mjs.map +1 -0
- package/dist/plugins/email-otp/routes.mjs +564 -0
- package/dist/plugins/email-otp/routes.mjs.map +1 -0
- package/dist/plugins/email-otp/types.d.mts +74 -0
- package/dist/plugins/email-otp/utils.mjs +17 -0
- package/dist/plugins/email-otp/utils.mjs.map +1 -0
- package/dist/plugins/generic-oauth/client.d.mts +19 -0
- package/dist/plugins/generic-oauth/client.mjs +14 -0
- package/dist/plugins/generic-oauth/client.mjs.map +1 -0
- package/dist/plugins/generic-oauth/error-codes.d.mts +5 -0
- package/dist/plugins/generic-oauth/error-codes.mjs +15 -0
- package/dist/plugins/generic-oauth/error-codes.mjs.map +1 -0
- package/dist/plugins/generic-oauth/index.d.mts +34 -0
- package/dist/plugins/generic-oauth/index.mjs +137 -0
- package/dist/plugins/generic-oauth/index.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/auth0.d.mts +37 -0
- package/dist/plugins/generic-oauth/providers/auth0.mjs +62 -0
- package/dist/plugins/generic-oauth/providers/auth0.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/gumroad.d.mts +32 -0
- package/dist/plugins/generic-oauth/providers/gumroad.mjs +60 -0
- package/dist/plugins/generic-oauth/providers/gumroad.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/hubspot.d.mts +37 -0
- package/dist/plugins/generic-oauth/providers/hubspot.mjs +60 -0
- package/dist/plugins/generic-oauth/providers/hubspot.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/index.d.mts +9 -0
- package/dist/plugins/generic-oauth/providers/index.mjs +11 -0
- package/dist/plugins/generic-oauth/providers/keycloak.d.mts +37 -0
- package/dist/plugins/generic-oauth/providers/keycloak.mjs +62 -0
- package/dist/plugins/generic-oauth/providers/keycloak.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/line.d.mts +55 -0
- package/dist/plugins/generic-oauth/providers/line.mjs +91 -0
- package/dist/plugins/generic-oauth/providers/line.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.d.mts +37 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.mjs +66 -0
- package/dist/plugins/generic-oauth/providers/microsoft-entra-id.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/okta.d.mts +37 -0
- package/dist/plugins/generic-oauth/providers/okta.mjs +62 -0
- package/dist/plugins/generic-oauth/providers/okta.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/patreon.d.mts +30 -0
- package/dist/plugins/generic-oauth/providers/patreon.mjs +59 -0
- package/dist/plugins/generic-oauth/providers/patreon.mjs.map +1 -0
- package/dist/plugins/generic-oauth/providers/slack.d.mts +30 -0
- package/dist/plugins/generic-oauth/providers/slack.mjs +61 -0
- package/dist/plugins/generic-oauth/providers/slack.mjs.map +1 -0
- package/dist/plugins/generic-oauth/routes.mjs +394 -0
- package/dist/plugins/generic-oauth/routes.mjs.map +1 -0
- package/dist/plugins/generic-oauth/types.d.mts +145 -0
- package/dist/plugins/haveibeenpwned/index.d.mts +21 -0
- package/dist/plugins/haveibeenpwned/index.mjs +56 -0
- package/dist/plugins/haveibeenpwned/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +68 -0
- package/dist/plugins/index.mjs +51 -0
- package/dist/plugins/jwt/adapter.mjs +27 -0
- package/dist/plugins/jwt/adapter.mjs.map +1 -0
- package/dist/plugins/jwt/client.d.mts +18 -0
- package/dist/plugins/jwt/client.mjs +19 -0
- package/dist/plugins/jwt/client.mjs.map +1 -0
- package/dist/plugins/jwt/index.d.mts +17 -0
- package/dist/plugins/jwt/index.mjs +202 -0
- package/dist/plugins/jwt/index.mjs.map +1 -0
- package/dist/plugins/jwt/schema.d.mts +5 -0
- package/dist/plugins/jwt/schema.mjs +23 -0
- package/dist/plugins/jwt/schema.mjs.map +1 -0
- package/dist/plugins/jwt/sign.d.mts +57 -0
- package/dist/plugins/jwt/sign.mjs +66 -0
- package/dist/plugins/jwt/sign.mjs.map +1 -0
- package/dist/plugins/jwt/types.d.mts +194 -0
- package/dist/plugins/jwt/utils.d.mts +42 -0
- package/dist/plugins/jwt/utils.mjs +64 -0
- package/dist/plugins/jwt/utils.mjs.map +1 -0
- package/dist/plugins/jwt/verify.d.mts +12 -0
- package/dist/plugins/jwt/verify.mjs +46 -0
- package/dist/plugins/jwt/verify.mjs.map +1 -0
- package/dist/plugins/last-login-method/client.d.mts +18 -0
- package/dist/plugins/last-login-method/client.mjs +32 -0
- package/dist/plugins/last-login-method/client.mjs.map +1 -0
- package/dist/plugins/last-login-method/index.d.mts +52 -0
- package/dist/plugins/last-login-method/index.mjs +77 -0
- package/dist/plugins/last-login-method/index.mjs.map +1 -0
- package/dist/plugins/magic-link/client.d.mts +5 -0
- package/dist/plugins/magic-link/client.mjs +11 -0
- package/dist/plugins/magic-link/client.mjs.map +1 -0
- package/dist/plugins/magic-link/index.d.mts +61 -0
- package/dist/plugins/magic-link/index.mjs +167 -0
- package/dist/plugins/magic-link/index.mjs.map +1 -0
- package/dist/plugins/magic-link/utils.mjs +12 -0
- package/dist/plugins/magic-link/utils.mjs.map +1 -0
- package/dist/plugins/mcp/authorize.mjs +133 -0
- package/dist/plugins/mcp/authorize.mjs.map +1 -0
- package/dist/plugins/mcp/index.d.mts +46 -0
- package/dist/plugins/mcp/index.mjs +717 -0
- package/dist/plugins/mcp/index.mjs.map +1 -0
- package/dist/plugins/multi-session/client.d.mts +8 -0
- package/dist/plugins/multi-session/client.mjs +20 -0
- package/dist/plugins/multi-session/client.mjs.map +1 -0
- package/dist/plugins/multi-session/error-codes.d.mts +5 -0
- package/dist/plugins/multi-session/error-codes.mjs +8 -0
- package/dist/plugins/multi-session/error-codes.mjs.map +1 -0
- package/dist/plugins/multi-session/index.d.mts +22 -0
- package/dist/plugins/multi-session/index.mjs +172 -0
- package/dist/plugins/multi-session/index.mjs.map +1 -0
- package/dist/plugins/oauth-proxy/index.d.mts +39 -0
- package/dist/plugins/oauth-proxy/index.mjs +305 -0
- package/dist/plugins/oauth-proxy/index.mjs.map +1 -0
- package/dist/plugins/oauth-proxy/utils.mjs +44 -0
- package/dist/plugins/oauth-proxy/utils.mjs.map +1 -0
- package/dist/plugins/oidc-provider/authorize.mjs +194 -0
- package/dist/plugins/oidc-provider/authorize.mjs.map +1 -0
- package/dist/plugins/oidc-provider/client.d.mts +8 -0
- package/dist/plugins/oidc-provider/client.mjs +11 -0
- package/dist/plugins/oidc-provider/client.mjs.map +1 -0
- package/dist/plugins/oidc-provider/error.mjs +17 -0
- package/dist/plugins/oidc-provider/error.mjs.map +1 -0
- package/dist/plugins/oidc-provider/index.d.mts +32 -0
- package/dist/plugins/oidc-provider/index.mjs +1093 -0
- package/dist/plugins/oidc-provider/index.mjs.map +1 -0
- package/dist/plugins/oidc-provider/schema.d.mts +26 -0
- package/dist/plugins/oidc-provider/schema.mjs +132 -0
- package/dist/plugins/oidc-provider/schema.mjs.map +1 -0
- package/dist/plugins/oidc-provider/types.d.mts +517 -0
- package/dist/plugins/oidc-provider/utils/prompt.mjs +19 -0
- package/dist/plugins/oidc-provider/utils/prompt.mjs.map +1 -0
- package/dist/plugins/oidc-provider/utils.mjs +15 -0
- package/dist/plugins/oidc-provider/utils.mjs.map +1 -0
- package/dist/plugins/one-tap/client.d.mts +159 -0
- package/dist/plugins/one-tap/client.mjs +214 -0
- package/dist/plugins/one-tap/client.mjs.map +1 -0
- package/dist/plugins/one-tap/index.d.mts +27 -0
- package/dist/plugins/one-tap/index.mjs +96 -0
- package/dist/plugins/one-tap/index.mjs.map +1 -0
- package/dist/plugins/one-time-token/client.d.mts +7 -0
- package/dist/plugins/one-time-token/client.mjs +11 -0
- package/dist/plugins/one-time-token/client.mjs.map +1 -0
- package/dist/plugins/one-time-token/index.d.mts +53 -0
- package/dist/plugins/one-time-token/index.mjs +82 -0
- package/dist/plugins/one-time-token/index.mjs.map +1 -0
- package/dist/plugins/one-time-token/utils.mjs +12 -0
- package/dist/plugins/one-time-token/utils.mjs.map +1 -0
- package/dist/plugins/open-api/generator.d.mts +115 -0
- package/dist/plugins/open-api/generator.mjs +315 -0
- package/dist/plugins/open-api/generator.mjs.map +1 -0
- package/dist/plugins/open-api/index.d.mts +45 -0
- package/dist/plugins/open-api/index.mjs +67 -0
- package/dist/plugins/open-api/index.mjs.map +1 -0
- package/dist/plugins/open-api/logo.mjs +15 -0
- package/dist/plugins/open-api/logo.mjs.map +1 -0
- package/dist/plugins/organization/access/index.d.mts +2 -0
- package/dist/plugins/organization/access/index.mjs +3 -0
- package/dist/plugins/organization/access/statement.d.mts +249 -0
- package/dist/plugins/organization/access/statement.mjs +81 -0
- package/dist/plugins/organization/access/statement.mjs.map +1 -0
- package/dist/plugins/organization/adapter.d.mts +205 -0
- package/dist/plugins/organization/adapter.mjs +624 -0
- package/dist/plugins/organization/adapter.mjs.map +1 -0
- package/dist/plugins/organization/call.mjs +19 -0
- package/dist/plugins/organization/call.mjs.map +1 -0
- package/dist/plugins/organization/client.d.mts +151 -0
- package/dist/plugins/organization/client.mjs +107 -0
- package/dist/plugins/organization/client.mjs.map +1 -0
- package/dist/plugins/organization/error-codes.d.mts +5 -0
- package/dist/plugins/organization/error-codes.mjs +65 -0
- package/dist/plugins/organization/error-codes.mjs.map +1 -0
- package/dist/plugins/organization/has-permission.mjs +35 -0
- package/dist/plugins/organization/has-permission.mjs.map +1 -0
- package/dist/plugins/organization/index.d.mts +5 -0
- package/dist/plugins/organization/index.mjs +4 -0
- package/dist/plugins/organization/organization.d.mts +252 -0
- package/dist/plugins/organization/organization.mjs +428 -0
- package/dist/plugins/organization/organization.mjs.map +1 -0
- package/dist/plugins/organization/permission.d.mts +26 -0
- package/dist/plugins/organization/permission.mjs +16 -0
- package/dist/plugins/organization/permission.mjs.map +1 -0
- package/dist/plugins/organization/routes/crud-access-control.d.mts +11 -0
- package/dist/plugins/organization/routes/crud-access-control.mjs +656 -0
- package/dist/plugins/organization/routes/crud-access-control.mjs.map +1 -0
- package/dist/plugins/organization/routes/crud-invites.d.mts +16 -0
- package/dist/plugins/organization/routes/crud-invites.mjs +555 -0
- package/dist/plugins/organization/routes/crud-invites.mjs.map +1 -0
- package/dist/plugins/organization/routes/crud-members.d.mts +13 -0
- package/dist/plugins/organization/routes/crud-members.mjs +473 -0
- package/dist/plugins/organization/routes/crud-members.mjs.map +1 -0
- package/dist/plugins/organization/routes/crud-org.d.mts +13 -0
- package/dist/plugins/organization/routes/crud-org.mjs +447 -0
- package/dist/plugins/organization/routes/crud-org.mjs.map +1 -0
- package/dist/plugins/organization/routes/crud-team.d.mts +15 -0
- package/dist/plugins/organization/routes/crud-team.mjs +676 -0
- package/dist/plugins/organization/routes/crud-team.mjs.map +1 -0
- package/dist/plugins/organization/schema.d.mts +376 -0
- package/dist/plugins/organization/schema.mjs +68 -0
- package/dist/plugins/organization/schema.mjs.map +1 -0
- package/dist/plugins/organization/types.d.mts +733 -0
- package/dist/plugins/phone-number/client.d.mts +8 -0
- package/dist/plugins/phone-number/client.mjs +20 -0
- package/dist/plugins/phone-number/client.mjs.map +1 -0
- package/dist/plugins/phone-number/error-codes.d.mts +5 -0
- package/dist/plugins/phone-number/error-codes.mjs +21 -0
- package/dist/plugins/phone-number/error-codes.mjs.map +1 -0
- package/dist/plugins/phone-number/index.d.mts +14 -0
- package/dist/plugins/phone-number/index.mjs +49 -0
- package/dist/plugins/phone-number/index.mjs.map +1 -0
- package/dist/plugins/phone-number/routes.mjs +459 -0
- package/dist/plugins/phone-number/routes.mjs.map +1 -0
- package/dist/plugins/phone-number/schema.d.mts +5 -0
- package/dist/plugins/phone-number/schema.mjs +20 -0
- package/dist/plugins/phone-number/schema.mjs.map +1 -0
- package/dist/plugins/phone-number/types.d.mts +118 -0
- package/dist/plugins/siwe/client.d.mts +5 -0
- package/dist/plugins/siwe/client.mjs +11 -0
- package/dist/plugins/siwe/client.mjs.map +1 -0
- package/dist/plugins/siwe/error-codes.mjs +13 -0
- package/dist/plugins/siwe/error-codes.mjs.map +1 -0
- package/dist/plugins/siwe/index.d.mts +26 -0
- package/dist/plugins/siwe/index.mjs +261 -0
- package/dist/plugins/siwe/index.mjs.map +1 -0
- package/dist/plugins/siwe/schema.d.mts +5 -0
- package/dist/plugins/siwe/schema.mjs +32 -0
- package/dist/plugins/siwe/schema.mjs.map +1 -0
- package/dist/plugins/siwe/types.d.mts +44 -0
- package/dist/plugins/two-factor/backup-codes/index.d.mts +91 -0
- package/dist/plugins/two-factor/backup-codes/index.mjs +277 -0
- package/dist/plugins/two-factor/backup-codes/index.mjs.map +1 -0
- package/dist/plugins/two-factor/client.d.mts +17 -0
- package/dist/plugins/two-factor/client.mjs +37 -0
- package/dist/plugins/two-factor/client.mjs.map +1 -0
- package/dist/plugins/two-factor/constant.mjs +8 -0
- package/dist/plugins/two-factor/constant.mjs.map +1 -0
- package/dist/plugins/two-factor/error-code.d.mts +5 -0
- package/dist/plugins/two-factor/error-code.mjs +18 -0
- package/dist/plugins/two-factor/error-code.mjs.map +1 -0
- package/dist/plugins/two-factor/index.d.mts +19 -0
- package/dist/plugins/two-factor/index.mjs +207 -0
- package/dist/plugins/two-factor/index.mjs.map +1 -0
- package/dist/plugins/two-factor/otp/index.d.mts +96 -0
- package/dist/plugins/two-factor/otp/index.mjs +199 -0
- package/dist/plugins/two-factor/otp/index.mjs.map +1 -0
- package/dist/plugins/two-factor/schema.d.mts +5 -0
- package/dist/plugins/two-factor/schema.mjs +36 -0
- package/dist/plugins/two-factor/schema.mjs.map +1 -0
- package/dist/plugins/two-factor/totp/index.d.mts +81 -0
- package/dist/plugins/two-factor/totp/index.mjs +157 -0
- package/dist/plugins/two-factor/totp/index.mjs.map +1 -0
- package/dist/plugins/two-factor/types.d.mts +65 -0
- package/dist/plugins/two-factor/utils.mjs +12 -0
- package/dist/plugins/two-factor/utils.mjs.map +1 -0
- package/dist/plugins/two-factor/verify-two-factor.mjs +76 -0
- package/dist/plugins/two-factor/verify-two-factor.mjs.map +1 -0
- package/dist/plugins/username/client.d.mts +7 -0
- package/dist/plugins/username/client.mjs +18 -0
- package/dist/plugins/username/client.mjs.map +1 -0
- package/dist/plugins/username/error-codes.d.mts +5 -0
- package/dist/plugins/username/error-codes.mjs +17 -0
- package/dist/plugins/username/error-codes.mjs.map +1 -0
- package/dist/plugins/username/index.d.mts +74 -0
- package/dist/plugins/username/index.mjs +237 -0
- package/dist/plugins/username/index.mjs.map +1 -0
- package/dist/plugins/username/schema.d.mts +9 -0
- package/dist/plugins/username/schema.mjs +26 -0
- package/dist/plugins/username/schema.mjs.map +1 -0
- package/dist/social-providers/index.d.mts +1 -0
- package/dist/social-providers/index.mjs +3 -0
- package/dist/state.d.mts +42 -0
- package/dist/state.mjs +107 -0
- package/dist/state.mjs.map +1 -0
- package/dist/test-utils/headers.d.mts +9 -0
- package/dist/test-utils/headers.mjs +24 -0
- package/dist/test-utils/headers.mjs.map +1 -0
- package/dist/test-utils/index.d.mts +3 -0
- package/dist/test-utils/index.mjs +4 -0
- package/dist/test-utils/test-instance.d.mts +181 -0
- package/dist/test-utils/test-instance.mjs +210 -0
- package/dist/test-utils/test-instance.mjs.map +1 -0
- package/dist/types/adapter.d.mts +24 -0
- package/dist/types/api.d.mts +62 -0
- package/dist/types/auth.d.mts +30 -0
- package/dist/types/helper.d.mts +21 -0
- package/dist/types/index.d.mts +11 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/models.d.mts +17 -0
- package/dist/types/plugins.d.mts +16 -0
- package/dist/utils/boolean.mjs +8 -0
- package/dist/utils/boolean.mjs.map +1 -0
- package/dist/utils/constants.mjs +6 -0
- package/dist/utils/constants.mjs.map +1 -0
- package/dist/utils/date.mjs +8 -0
- package/dist/utils/date.mjs.map +1 -0
- package/dist/utils/get-request-ip.d.mts +7 -0
- package/dist/utils/get-request-ip.mjs +23 -0
- package/dist/utils/get-request-ip.mjs.map +1 -0
- package/dist/utils/hashing.mjs +21 -0
- package/dist/utils/hashing.mjs.map +1 -0
- package/dist/utils/hide-metadata.d.mts +7 -0
- package/dist/utils/hide-metadata.mjs +6 -0
- package/dist/utils/hide-metadata.mjs.map +1 -0
- package/dist/utils/index.d.mts +3 -0
- package/dist/utils/index.mjs +5 -0
- package/dist/utils/is-api-error.d.mts +7 -0
- package/dist/utils/is-api-error.mjs +11 -0
- package/dist/utils/is-api-error.mjs.map +1 -0
- package/dist/utils/is-atom.mjs +8 -0
- package/dist/utils/is-atom.mjs.map +1 -0
- package/dist/utils/is-promise.mjs +8 -0
- package/dist/utils/is-promise.mjs.map +1 -0
- package/dist/utils/middleware-response.mjs +6 -0
- package/dist/utils/middleware-response.mjs.map +1 -0
- package/dist/utils/password.mjs +26 -0
- package/dist/utils/password.mjs.map +1 -0
- package/dist/utils/plugin-helper.mjs +17 -0
- package/dist/utils/plugin-helper.mjs.map +1 -0
- package/dist/utils/shim.mjs +24 -0
- package/dist/utils/shim.mjs.map +1 -0
- package/dist/utils/time.d.mts +49 -0
- package/dist/utils/time.mjs +100 -0
- package/dist/utils/time.mjs.map +1 -0
- package/dist/utils/url.mjs +92 -0
- package/dist/utils/url.mjs.map +1 -0
- package/dist/utils/wildcard.mjs +108 -0
- package/dist/utils/wildcard.mjs.map +1 -0
- package/package.json +601 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { getOrigin } from "../../utils/url.mjs";
|
|
2
|
+
import { originCheck } from "../../api/middlewares/origin-check.mjs";
|
|
3
|
+
import { symmetricDecrypt, symmetricEncrypt } from "../../crypto/index.mjs";
|
|
4
|
+
import { parseSetCookieHeader, stripSecureCookiePrefix } from "../../cookies/cookie-utils.mjs";
|
|
5
|
+
import "../../cookies/index.mjs";
|
|
6
|
+
import "../../api/index.mjs";
|
|
7
|
+
import { parseJSON } from "../../client/parser.mjs";
|
|
8
|
+
import { checkSkipProxy, resolveCurrentURL, stripTrailingSlash } from "./utils.mjs";
|
|
9
|
+
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
10
|
+
import * as z from "zod";
|
|
11
|
+
|
|
12
|
+
//#region src/plugins/oauth-proxy/index.ts
|
|
13
|
+
const oAuthProxyQuerySchema = z.object({
|
|
14
|
+
callbackURL: z.string().meta({ description: "The URL to redirect to after the proxy" }),
|
|
15
|
+
cookies: z.string().meta({ description: "The cookies to set after the proxy" })
|
|
16
|
+
});
|
|
17
|
+
const oAuthProxy = (opts) => {
|
|
18
|
+
const maxAge = opts?.maxAge ?? 60;
|
|
19
|
+
return {
|
|
20
|
+
id: "oauth-proxy",
|
|
21
|
+
options: opts,
|
|
22
|
+
endpoints: { oAuthProxy: createAuthEndpoint("/oauth-proxy-callback", {
|
|
23
|
+
method: "GET",
|
|
24
|
+
operationId: "oauthProxyCallback",
|
|
25
|
+
query: oAuthProxyQuerySchema,
|
|
26
|
+
use: [originCheck((ctx) => ctx.query.callbackURL)],
|
|
27
|
+
metadata: { openapi: {
|
|
28
|
+
operationId: "oauthProxyCallback",
|
|
29
|
+
description: "OAuth Proxy Callback",
|
|
30
|
+
parameters: [{
|
|
31
|
+
in: "query",
|
|
32
|
+
name: "callbackURL",
|
|
33
|
+
required: true,
|
|
34
|
+
description: "The URL to redirect to after the proxy"
|
|
35
|
+
}, {
|
|
36
|
+
in: "query",
|
|
37
|
+
name: "cookies",
|
|
38
|
+
required: true,
|
|
39
|
+
description: "The cookies to set after the proxy"
|
|
40
|
+
}],
|
|
41
|
+
responses: { 302: {
|
|
42
|
+
description: "Redirect",
|
|
43
|
+
headers: { Location: {
|
|
44
|
+
description: "The URL to redirect to",
|
|
45
|
+
schema: { type: "string" }
|
|
46
|
+
} }
|
|
47
|
+
} }
|
|
48
|
+
} }
|
|
49
|
+
}, async (ctx) => {
|
|
50
|
+
let decryptedPayload = null;
|
|
51
|
+
try {
|
|
52
|
+
decryptedPayload = await symmetricDecrypt({
|
|
53
|
+
key: ctx.context.secret,
|
|
54
|
+
data: ctx.query.cookies
|
|
55
|
+
});
|
|
56
|
+
} catch (e) {
|
|
57
|
+
ctx.context.logger.error("Failed to decrypt OAuth proxy cookies:", e);
|
|
58
|
+
}
|
|
59
|
+
if (!decryptedPayload) {
|
|
60
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;
|
|
61
|
+
throw ctx.redirect(`${errorURL}?error=OAuthProxy - Invalid cookies or secret`);
|
|
62
|
+
}
|
|
63
|
+
let payload;
|
|
64
|
+
try {
|
|
65
|
+
payload = parseJSON(decryptedPayload);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
ctx.context.logger.error("Failed to parse OAuth proxy payload:", e);
|
|
68
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;
|
|
69
|
+
throw ctx.redirect(`${errorURL}?error=OAuthProxy - Invalid payload format`);
|
|
70
|
+
}
|
|
71
|
+
if (!payload.cookies || typeof payload.cookies !== "string" || typeof payload.timestamp !== "number") {
|
|
72
|
+
ctx.context.logger.error("OAuth proxy payload missing required fields");
|
|
73
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;
|
|
74
|
+
throw ctx.redirect(`${errorURL}?error=OAuthProxy - Invalid payload structure`);
|
|
75
|
+
}
|
|
76
|
+
const age = (Date.now() - payload.timestamp) / 1e3;
|
|
77
|
+
if (age > maxAge || age < -10) {
|
|
78
|
+
ctx.context.logger.error(`OAuth proxy payload expired or invalid (age: ${age}s, maxAge: ${maxAge}s)`);
|
|
79
|
+
const errorURL = ctx.context.options.onAPIError?.errorURL || `${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;
|
|
80
|
+
throw ctx.redirect(`${errorURL}?error=OAuthProxy - Payload expired or invalid`);
|
|
81
|
+
}
|
|
82
|
+
const decryptedCookies = payload.cookies;
|
|
83
|
+
const isSecureContext = resolveCurrentURL(ctx, opts).protocol === "https:";
|
|
84
|
+
const parsedCookies = parseSetCookieHeader(decryptedCookies);
|
|
85
|
+
const processedCookies = Array.from(parsedCookies.entries()).map(([name, attrs]) => {
|
|
86
|
+
const options = {};
|
|
87
|
+
if (attrs.path) options.path = attrs.path;
|
|
88
|
+
if (attrs.expires) options.expires = attrs.expires;
|
|
89
|
+
if (attrs.samesite) options.sameSite = attrs.samesite;
|
|
90
|
+
if (attrs.httponly) options.httpOnly = true;
|
|
91
|
+
if (attrs["max-age"] !== void 0) options.maxAge = attrs["max-age"];
|
|
92
|
+
if (isSecureContext) options.secure = true;
|
|
93
|
+
const cookieName = isSecureContext ? name : stripSecureCookiePrefix(name);
|
|
94
|
+
let cookieValue;
|
|
95
|
+
try {
|
|
96
|
+
cookieValue = decodeURIComponent(attrs.value);
|
|
97
|
+
} catch {
|
|
98
|
+
cookieValue = attrs.value;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
name: cookieName,
|
|
102
|
+
value: cookieValue,
|
|
103
|
+
options
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
for (const cookie of processedCookies) ctx.setCookie(cookie.name, cookie.value, cookie.options);
|
|
107
|
+
throw ctx.redirect(ctx.query.callbackURL);
|
|
108
|
+
}) },
|
|
109
|
+
hooks: {
|
|
110
|
+
before: [
|
|
111
|
+
{
|
|
112
|
+
matcher(context) {
|
|
113
|
+
return !!(context.path?.startsWith("/sign-in/social") || context.path?.startsWith("/sign-in/oauth2"));
|
|
114
|
+
},
|
|
115
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
116
|
+
if (checkSkipProxy(ctx, opts)) return;
|
|
117
|
+
const currentURL = resolveCurrentURL(ctx, opts);
|
|
118
|
+
const productionURL = opts?.productionURL;
|
|
119
|
+
const originalCallbackURL = ctx.body?.callbackURL || ctx.context.baseURL;
|
|
120
|
+
if (productionURL) {
|
|
121
|
+
const productionBaseURL = `${stripTrailingSlash(productionURL)}${ctx.context.options.basePath || "/api/auth"}`;
|
|
122
|
+
ctx.context.baseURL = productionBaseURL;
|
|
123
|
+
}
|
|
124
|
+
const newCallbackURL = `${stripTrailingSlash(currentURL.origin)}${ctx.context.options.basePath || "/api/auth"}/oauth-proxy-callback?callbackURL=${encodeURIComponent(originalCallbackURL)}`;
|
|
125
|
+
if (!ctx.body) return;
|
|
126
|
+
ctx.body.callbackURL = newCallbackURL;
|
|
127
|
+
})
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
matcher(context) {
|
|
131
|
+
return !!(context.path?.startsWith("/callback") || context.path?.startsWith("/oauth2/callback"));
|
|
132
|
+
},
|
|
133
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
134
|
+
const state = ctx.query?.state || ctx.body?.state;
|
|
135
|
+
if (!state || typeof state !== "string") return;
|
|
136
|
+
let statePackage;
|
|
137
|
+
try {
|
|
138
|
+
statePackage = parseJSON(await symmetricDecrypt({
|
|
139
|
+
key: ctx.context.secret,
|
|
140
|
+
data: state
|
|
141
|
+
}));
|
|
142
|
+
} catch {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!statePackage.isOAuthProxy || !statePackage.state || !statePackage.stateCookie) return;
|
|
146
|
+
let stateCookieValue;
|
|
147
|
+
try {
|
|
148
|
+
stateCookieValue = await symmetricDecrypt({
|
|
149
|
+
key: ctx.context.secret,
|
|
150
|
+
data: statePackage.stateCookie
|
|
151
|
+
});
|
|
152
|
+
parseJSON(stateCookieValue);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
ctx.context.logger.error("Failed to decrypt OAuth proxy state cookie:", e);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
ctx.context._oauthProxySnapshot = {
|
|
158
|
+
storeStateStrategy: ctx.context.oauthConfig.storeStateStrategy,
|
|
159
|
+
skipStateCookieCheck: ctx.context.oauthConfig.skipStateCookieCheck,
|
|
160
|
+
internalAdapter: ctx.context.internalAdapter
|
|
161
|
+
};
|
|
162
|
+
const originalAdapter = ctx.context.internalAdapter;
|
|
163
|
+
const capturedStatePackage = statePackage;
|
|
164
|
+
ctx.context.oauthConfig.storeStateStrategy = "database";
|
|
165
|
+
ctx.context.internalAdapter = {
|
|
166
|
+
...ctx.context.internalAdapter,
|
|
167
|
+
findVerificationValue: async (identifier) => {
|
|
168
|
+
if (identifier === capturedStatePackage.state) return {
|
|
169
|
+
id: `oauth-proxy-${capturedStatePackage.state}`,
|
|
170
|
+
identifier: capturedStatePackage.state,
|
|
171
|
+
value: stateCookieValue,
|
|
172
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
173
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
174
|
+
expiresAt: new Date(Date.now() + 600 * 1e3)
|
|
175
|
+
};
|
|
176
|
+
return originalAdapter.findVerificationValue(identifier);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
if (ctx.query?.state) ctx.query.state = statePackage.state;
|
|
180
|
+
if (ctx.body?.state) ctx.body.state = statePackage.state;
|
|
181
|
+
ctx.context.oauthConfig.skipStateCookieCheck = true;
|
|
182
|
+
})
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
matcher() {
|
|
186
|
+
return true;
|
|
187
|
+
},
|
|
188
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
189
|
+
if (ctx.path !== "/callback/:id") return;
|
|
190
|
+
if (ctx.context.oauthConfig.storeStateStrategy === "cookie") return;
|
|
191
|
+
if (ctx.context._oauthProxySnapshot) return;
|
|
192
|
+
const state = ctx.query?.state || ctx.body?.state;
|
|
193
|
+
if (!state) return;
|
|
194
|
+
const data = await ctx.context.internalAdapter.findVerificationValue(state);
|
|
195
|
+
if (!data) return;
|
|
196
|
+
let parsedState;
|
|
197
|
+
try {
|
|
198
|
+
parsedState = parseJSON(data.value);
|
|
199
|
+
} catch {
|
|
200
|
+
parsedState = void 0;
|
|
201
|
+
}
|
|
202
|
+
if (!parsedState?.callbackURL?.includes("/oauth-proxy-callback")) return;
|
|
203
|
+
ctx.context._oauthProxySnapshot = {
|
|
204
|
+
storeStateStrategy: ctx.context.oauthConfig.storeStateStrategy,
|
|
205
|
+
skipStateCookieCheck: ctx.context.oauthConfig.skipStateCookieCheck,
|
|
206
|
+
internalAdapter: ctx.context.internalAdapter
|
|
207
|
+
};
|
|
208
|
+
ctx.context.oauthConfig.skipStateCookieCheck = true;
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
after: [
|
|
213
|
+
{
|
|
214
|
+
matcher(context) {
|
|
215
|
+
return !!(context.path?.startsWith("/sign-in/social") || context.path?.startsWith("/sign-in/oauth2"));
|
|
216
|
+
},
|
|
217
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
218
|
+
if (checkSkipProxy(ctx, opts)) return;
|
|
219
|
+
if (ctx.context.oauthConfig.storeStateStrategy !== "cookie") return;
|
|
220
|
+
const signInResponse = ctx.context.returned;
|
|
221
|
+
if (!signInResponse || typeof signInResponse !== "object" || !("url" in signInResponse)) return;
|
|
222
|
+
const { url: providerURL } = signInResponse;
|
|
223
|
+
if (typeof providerURL !== "string") return;
|
|
224
|
+
const oauthURL = new URL(providerURL);
|
|
225
|
+
const originalState = oauthURL.searchParams.get("state");
|
|
226
|
+
if (!originalState) return;
|
|
227
|
+
const setCookieHeader = ctx.context.responseHeaders?.get("set-cookie");
|
|
228
|
+
if (!setCookieHeader) return;
|
|
229
|
+
const stateCookie = ctx.context.createAuthCookie("oauth_state");
|
|
230
|
+
const stateCookieAttrs = parseSetCookieHeader(setCookieHeader).get(stateCookie.name);
|
|
231
|
+
if (!stateCookieAttrs?.value) return;
|
|
232
|
+
const stateCookieValue = stateCookieAttrs.value;
|
|
233
|
+
try {
|
|
234
|
+
const statePackage = {
|
|
235
|
+
state: originalState,
|
|
236
|
+
stateCookie: stateCookieValue,
|
|
237
|
+
isOAuthProxy: true
|
|
238
|
+
};
|
|
239
|
+
const encryptedPackage = await symmetricEncrypt({
|
|
240
|
+
key: ctx.context.secret,
|
|
241
|
+
data: JSON.stringify(statePackage)
|
|
242
|
+
});
|
|
243
|
+
oauthURL.searchParams.set("state", encryptedPackage);
|
|
244
|
+
ctx.context.returned = {
|
|
245
|
+
...signInResponse,
|
|
246
|
+
url: oauthURL.toString()
|
|
247
|
+
};
|
|
248
|
+
} catch (e) {
|
|
249
|
+
ctx.context.logger.error("Failed to encrypt OAuth proxy state package:", e);
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
matcher(context) {
|
|
255
|
+
return !!(context.path?.startsWith("/callback") || context.path?.startsWith("/oauth2/callback"));
|
|
256
|
+
},
|
|
257
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
258
|
+
const headers = ctx.context.responseHeaders;
|
|
259
|
+
const location = headers?.get("location");
|
|
260
|
+
if (!location?.includes("/oauth-proxy-callback?callbackURL") || !location.startsWith("http")) return;
|
|
261
|
+
const productionOrigin = getOrigin(opts?.productionURL || ctx.context.options.baseURL || ctx.context.baseURL);
|
|
262
|
+
const locationURL = new URL(location);
|
|
263
|
+
if (locationURL.origin === productionOrigin) {
|
|
264
|
+
const newLocation = locationURL.searchParams.get("callbackURL");
|
|
265
|
+
if (!newLocation) return;
|
|
266
|
+
ctx.setHeader("location", newLocation);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const setCookies = headers?.get("set-cookie");
|
|
270
|
+
if (!setCookies) return;
|
|
271
|
+
const payload = {
|
|
272
|
+
cookies: setCookies,
|
|
273
|
+
timestamp: Date.now()
|
|
274
|
+
};
|
|
275
|
+
const encryptedCookies = await symmetricEncrypt({
|
|
276
|
+
key: ctx.context.secret,
|
|
277
|
+
data: JSON.stringify(payload)
|
|
278
|
+
});
|
|
279
|
+
const locationWithCookies = `${location}&cookies=${encodeURIComponent(encryptedCookies)}`;
|
|
280
|
+
ctx.setHeader("location", locationWithCookies);
|
|
281
|
+
})
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
matcher(context) {
|
|
285
|
+
return !!(context.path?.startsWith("/callback") || context.path?.startsWith("/oauth2/callback"));
|
|
286
|
+
},
|
|
287
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
288
|
+
const contextWithSnapshot = ctx.context;
|
|
289
|
+
const snapshot = contextWithSnapshot._oauthProxySnapshot;
|
|
290
|
+
if (snapshot) {
|
|
291
|
+
ctx.context.oauthConfig.storeStateStrategy = snapshot.storeStateStrategy;
|
|
292
|
+
ctx.context.oauthConfig.skipStateCookieCheck = snapshot.skipStateCookieCheck;
|
|
293
|
+
ctx.context.internalAdapter = snapshot.internalAdapter;
|
|
294
|
+
contextWithSnapshot._oauthProxySnapshot = void 0;
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
//#endregion
|
|
304
|
+
export { oAuthProxy };
|
|
305
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/oauth-proxy/index.ts"],"sourcesContent":["import type { BetterAuthPlugin } from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport type { CookieOptions } from \"better-call\";\nimport * as z from \"zod\";\nimport { originCheck } from \"../../api\";\nimport { parseJSON } from \"../../client/parser\";\nimport { parseSetCookieHeader, stripSecureCookiePrefix } from \"../../cookies\";\nimport { symmetricDecrypt, symmetricEncrypt } from \"../../crypto\";\nimport { getOrigin } from \"../../utils/url\";\nimport type { AuthContextWithSnapshot, OAuthProxyStatePackage } from \"./types\";\nimport { checkSkipProxy, resolveCurrentURL, stripTrailingSlash } from \"./utils\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\t\"oauth-proxy\": {\n\t\t\tcreator: typeof oAuthProxy;\n\t\t};\n\t}\n}\n\nexport interface OAuthProxyOptions {\n\t/**\n\t * The current URL of the application.\n\t * The plugin will attempt to infer the current URL from your environment\n\t * by checking the base URL from popular hosting providers,\n\t * from the request URL if invoked by a client,\n\t * or as a fallback, from the `baseURL` in your auth config.\n\t * If the URL is not inferred correctly, you can provide a value here.\"\n\t */\n\tcurrentURL?: string | undefined;\n\t/**\n\t * If a request in a production url it won't be proxied.\n\t *\n\t * default to `BETTER_AUTH_URL`\n\t */\n\tproductionURL?: string | undefined;\n\t/**\n\t * Maximum age in seconds for the encrypted cookies payload.\n\t * Payloads older than this will be rejected to prevent replay attacks.\n\t *\n\t * Keep this value short (e.g., 30-60 seconds) to minimize the window\n\t * for potential replay attacks while still allowing normal OAuth flows.\n\t *\n\t * @default 60 (1 minute)\n\t */\n\tmaxAge?: number | undefined;\n}\n\ninterface EncryptedCookiesPayload {\n\tcookies: string;\n\ttimestamp: number;\n}\n\nconst oAuthProxyQuerySchema = z.object({\n\tcallbackURL: z.string().meta({\n\t\tdescription: \"The URL to redirect to after the proxy\",\n\t}),\n\tcookies: z.string().meta({\n\t\tdescription: \"The cookies to set after the proxy\",\n\t}),\n});\n\nexport const oAuthProxy = <O extends OAuthProxyOptions>(opts?: O) => {\n\tconst maxAge = opts?.maxAge ?? 60; // Default 60 seconds\n\n\treturn {\n\t\tid: \"oauth-proxy\",\n\t\toptions: opts as NoInfer<O>,\n\t\tendpoints: {\n\t\t\toAuthProxy: createAuthEndpoint(\n\t\t\t\t\"/oauth-proxy-callback\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\toperationId: \"oauthProxyCallback\",\n\t\t\t\t\tquery: oAuthProxyQuerySchema,\n\t\t\t\t\tuse: [originCheck((ctx) => ctx.query.callbackURL)],\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"oauthProxyCallback\",\n\t\t\t\t\t\t\tdescription: \"OAuth Proxy Callback\",\n\t\t\t\t\t\t\tparameters: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tin: \"query\",\n\t\t\t\t\t\t\t\t\tname: \"callbackURL\",\n\t\t\t\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\t\t\t\tdescription: \"The URL to redirect to after the proxy\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tin: \"query\",\n\t\t\t\t\t\t\t\t\tname: \"cookies\",\n\t\t\t\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\t\t\t\tdescription: \"The cookies to set after the proxy\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t302: {\n\t\t\t\t\t\t\t\t\tdescription: \"Redirect\",\n\t\t\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t\t\tLocation: {\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The URL to redirect to\",\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tlet decryptedPayload: string | null = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdecryptedPayload = await symmetricDecrypt({\n\t\t\t\t\t\t\tkey: ctx.context.secret,\n\t\t\t\t\t\t\tdata: ctx.query.cookies,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"Failed to decrypt OAuth proxy cookies:\",\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!decryptedPayload) {\n\t\t\t\t\t\tconst errorURL =\n\t\t\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t\t\t`${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;\n\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${errorURL}?error=OAuthProxy - Invalid cookies or secret`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet payload: EncryptedCookiesPayload;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpayload = parseJSON<EncryptedCookiesPayload>(decryptedPayload);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tctx.context.logger.error(\"Failed to parse OAuth proxy payload:\", e);\n\t\t\t\t\t\tconst errorURL =\n\t\t\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t\t\t`${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;\n\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${errorURL}?error=OAuthProxy - Invalid payload format`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t!payload.cookies ||\n\t\t\t\t\t\ttypeof payload.cookies !== \"string\" ||\n\t\t\t\t\t\ttypeof payload.timestamp !== \"number\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\"OAuth proxy payload missing required fields\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst errorURL =\n\t\t\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t\t\t`${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;\n\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${errorURL}?error=OAuthProxy - Invalid payload structure`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\tconst age = (now - payload.timestamp) / 1000;\n\n\t\t\t\t\t// Allow up to 10 seconds of future skew for clock differences\n\t\t\t\t\tif (age > maxAge || age < -10) {\n\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t`OAuth proxy payload expired or invalid (age: ${age}s, maxAge: ${maxAge}s)`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst errorURL =\n\t\t\t\t\t\t\tctx.context.options.onAPIError?.errorURL ||\n\t\t\t\t\t\t\t`${stripTrailingSlash(ctx.context.options.baseURL)}/api/auth/error`;\n\n\t\t\t\t\t\tthrow ctx.redirect(\n\t\t\t\t\t\t\t`${errorURL}?error=OAuthProxy - Payload expired or invalid`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst decryptedCookies = payload.cookies;\n\n\t\t\t\t\tconst currentURL = resolveCurrentURL(ctx, opts);\n\t\t\t\t\tconst isSecureContext = currentURL.protocol === \"https:\";\n\n\t\t\t\t\tconst parsedCookies = parseSetCookieHeader(decryptedCookies);\n\t\t\t\t\tconst processedCookies = Array.from(parsedCookies.entries()).map(\n\t\t\t\t\t\t([name, attrs]) => {\n\t\t\t\t\t\t\tconst options: CookieOptions = {};\n\t\t\t\t\t\t\tif (attrs.path) {\n\t\t\t\t\t\t\t\toptions.path = attrs.path;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (attrs.expires) {\n\t\t\t\t\t\t\t\toptions.expires = attrs.expires;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (attrs.samesite) {\n\t\t\t\t\t\t\t\toptions.sameSite = attrs.samesite;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (attrs.httponly) {\n\t\t\t\t\t\t\t\toptions.httpOnly = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (attrs[\"max-age\"] !== undefined) {\n\t\t\t\t\t\t\t\toptions.maxAge = attrs[\"max-age\"];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isSecureContext) {\n\t\t\t\t\t\t\t\toptions.secure = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Remove __Secure- or __Host- prefix for non-HTTPS contexts\n\t\t\t\t\t\t\tconst cookieName = isSecureContext\n\t\t\t\t\t\t\t\t? name\n\t\t\t\t\t\t\t\t: stripSecureCookiePrefix(name);\n\n\t\t\t\t\t\t\t// URI-decoded value because `ctx.setCookie` will URI-encode it again\n\t\t\t\t\t\t\tlet cookieValue: string;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tcookieValue = decodeURIComponent(attrs.value);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tcookieValue = attrs.value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tname: cookieName,\n\t\t\t\t\t\t\t\tvalue: cookieValue,\n\t\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\n\t\t\t\t\tfor (const cookie of processedCookies) {\n\t\t\t\t\t\t// using `ctx.setHeader` overrides previous Set-Cookie headers\n\t\t\t\t\t\t// so use ctx.setCookie helper instead\n\t\t\t\t\t\t// https://github.com/Bekacru/better-call/blob/d27ac20e64b329a4851e97adf864098a9bc2a260/src/context.ts#L217\n\t\t\t\t\t\tctx.setCookie(cookie.name, cookie.value, cookie.options);\n\t\t\t\t\t}\n\t\t\t\t\tthrow ctx.redirect(ctx.query.callbackURL);\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\thooks: {\n\t\t\tbefore: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn !!(\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/sign-in/social\") ||\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/sign-in/oauth2\")\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst skipProxy = checkSkipProxy(ctx, opts);\n\t\t\t\t\t\tif (skipProxy) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst currentURL = resolveCurrentURL(ctx, opts);\n\t\t\t\t\t\tconst productionURL = opts?.productionURL;\n\t\t\t\t\t\tconst originalCallbackURL =\n\t\t\t\t\t\t\tctx.body?.callbackURL || ctx.context.baseURL;\n\n\t\t\t\t\t\t// Override baseURL to production so redirect_uri points to production\n\t\t\t\t\t\t// This ensures OAuth provider callbacks go to the production server\n\t\t\t\t\t\tif (productionURL) {\n\t\t\t\t\t\t\tconst productionBaseURL = `${stripTrailingSlash(productionURL)}${ctx.context.options.basePath || \"/api/auth\"}`;\n\t\t\t\t\t\t\tctx.context.baseURL = productionBaseURL;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Construct proxy callback URL\n\t\t\t\t\t\tconst newCallbackURL = `${stripTrailingSlash(currentURL.origin)}${\n\t\t\t\t\t\t\tctx.context.options.basePath || \"/api/auth\"\n\t\t\t\t\t\t}/oauth-proxy-callback?callbackURL=${encodeURIComponent(\n\t\t\t\t\t\t\toriginalCallbackURL,\n\t\t\t\t\t\t)}`;\n\n\t\t\t\t\t\tif (!ctx.body) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tctx.body.callbackURL = newCallbackURL;\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn !!(\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/callback\") ||\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/oauth2/callback\")\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst state = ctx.query?.state || ctx.body?.state;\n\t\t\t\t\t\tif (!state || typeof state !== \"string\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Try to decrypt and parse OAuth proxy state package\n\t\t\t\t\t\tlet statePackage: OAuthProxyStatePackage | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst decryptedPackage = await symmetricDecrypt({\n\t\t\t\t\t\t\t\tkey: ctx.context.secret,\n\t\t\t\t\t\t\t\tdata: state,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstatePackage =\n\t\t\t\t\t\t\t\tparseJSON<OAuthProxyStatePackage>(decryptedPackage);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Not an OAuth proxy state, continue normally\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!statePackage.isOAuthProxy ||\n\t\t\t\t\t\t\t!statePackage.state ||\n\t\t\t\t\t\t\t!statePackage.stateCookie\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Decrypt the state cookie content\n\t\t\t\t\t\tlet stateCookieValue: string;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstateCookieValue = await symmetricDecrypt({\n\t\t\t\t\t\t\t\tkey: ctx.context.secret,\n\t\t\t\t\t\t\t\tdata: statePackage.stateCookie,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tparseJSON(stateCookieValue);\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\"Failed to decrypt OAuth proxy state cookie:\",\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Snapshot original configuration for restoration in after hook\n\t\t\t\t\t\t(ctx.context as AuthContextWithSnapshot)._oauthProxySnapshot = {\n\t\t\t\t\t\t\tstoreStateStrategy: ctx.context.oauthConfig.storeStateStrategy,\n\t\t\t\t\t\t\tskipStateCookieCheck:\n\t\t\t\t\t\t\t\tctx.context.oauthConfig.skipStateCookieCheck,\n\t\t\t\t\t\t\tinternalAdapter: ctx.context.internalAdapter,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Temporarily switch to database mode and inject verification value\n\t\t\t\t\t\t// This allows the OAuth callback handler to retrieve state data without database\n\t\t\t\t\t\tconst originalAdapter = ctx.context.internalAdapter;\n\t\t\t\t\t\tconst capturedStatePackage = statePackage;\n\t\t\t\t\t\tctx.context.oauthConfig.storeStateStrategy = \"database\";\n\t\t\t\t\t\tctx.context.internalAdapter = {\n\t\t\t\t\t\t\t...ctx.context.internalAdapter,\n\t\t\t\t\t\t\tfindVerificationValue: async (identifier: string) => {\n\t\t\t\t\t\t\t\tif (identifier === capturedStatePackage.state) {\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tid: `oauth-proxy-${capturedStatePackage.state}`,\n\t\t\t\t\t\t\t\t\t\tidentifier: capturedStatePackage.state,\n\t\t\t\t\t\t\t\t\t\tvalue: stateCookieValue,\n\t\t\t\t\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t\t\t\t// Align expiration time with `generateState` in oauth2\n\t\t\t\t\t\t\t\t\t\texpiresAt: new Date(Date.now() + 10 * 60 * 1000),\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn originalAdapter.findVerificationValue(identifier);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Restore original state parameter\n\t\t\t\t\t\tif (ctx.query?.state) {\n\t\t\t\t\t\t\tctx.query.state = statePackage.state;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ctx.body?.state) {\n\t\t\t\t\t\t\tctx.body.state = statePackage.state;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Enable skipStateCookieCheck for database mode\n\t\t\t\t\t\tctx.context.oauthConfig.skipStateCookieCheck = true;\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmatcher() {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tif (ctx.path !== \"/callback/:id\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ctx.context.oauthConfig.storeStateStrategy === \"cookie\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Skip if OAuth proxy stateless flow already handled by previous hook\n\t\t\t\t\t\tif ((ctx.context as AuthContextWithSnapshot)._oauthProxySnapshot) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst state = ctx.query?.state || ctx.body?.state;\n\t\t\t\t\t\tif (!state) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst data =\n\t\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(state);\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet parsedState: { callbackURL?: string } | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tparsedState = parseJSON<{ callbackURL?: string }>(data.value);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tparsedState = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!parsedState?.callbackURL?.includes(\"/oauth-proxy-callback\")) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Snapshot original configuration for restoration in after hook\n\t\t\t\t\t\t(ctx.context as AuthContextWithSnapshot)._oauthProxySnapshot = {\n\t\t\t\t\t\t\tstoreStateStrategy: ctx.context.oauthConfig.storeStateStrategy,\n\t\t\t\t\t\t\tskipStateCookieCheck:\n\t\t\t\t\t\t\t\tctx.context.oauthConfig.skipStateCookieCheck,\n\t\t\t\t\t\t\tinternalAdapter: ctx.context.internalAdapter,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tctx.context.oauthConfig.skipStateCookieCheck = true;\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn !!(\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/sign-in/social\") ||\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/sign-in/oauth2\")\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst skipProxy = checkSkipProxy(ctx, opts);\n\t\t\t\t\t\tif (skipProxy) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Only process in stateless mode\n\t\t\t\t\t\tif (ctx.context.oauthConfig.storeStateStrategy !== \"cookie\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract OAuth provider URL from sign-in response\n\t\t\t\t\t\tconst signInResponse = ctx.context.returned;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!signInResponse ||\n\t\t\t\t\t\t\ttypeof signInResponse !== \"object\" ||\n\t\t\t\t\t\t\t!(\"url\" in signInResponse)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst { url: providerURL } = signInResponse;\n\t\t\t\t\t\tif (typeof providerURL !== \"string\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Parse provider URL and extract state parameter\n\t\t\t\t\t\tconst oauthURL = new URL(providerURL);\n\t\t\t\t\t\tconst originalState = oauthURL.searchParams.get(\"state\");\n\t\t\t\t\t\tif (!originalState) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract state cookie from response headers\n\t\t\t\t\t\tconst headers = ctx.context.responseHeaders;\n\t\t\t\t\t\tconst setCookieHeader = headers?.get(\"set-cookie\");\n\t\t\t\t\t\tif (!setCookieHeader) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst stateCookie = ctx.context.createAuthCookie(\"oauth_state\");\n\t\t\t\t\t\tconst parsedStateCookies = parseSetCookieHeader(setCookieHeader);\n\t\t\t\t\t\tconst stateCookieAttrs = parsedStateCookies.get(stateCookie.name);\n\t\t\t\t\t\tif (!stateCookieAttrs?.value) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst stateCookieValue = stateCookieAttrs.value;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Create and encrypt state package\n\t\t\t\t\t\t\tconst statePackage: OAuthProxyStatePackage = {\n\t\t\t\t\t\t\t\tstate: originalState,\n\t\t\t\t\t\t\t\tstateCookie: stateCookieValue,\n\t\t\t\t\t\t\t\tisOAuthProxy: true,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tconst encryptedPackage = await symmetricEncrypt({\n\t\t\t\t\t\t\t\tkey: ctx.context.secret,\n\t\t\t\t\t\t\t\tdata: JSON.stringify(statePackage),\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Replace state parameter with encrypted package\n\t\t\t\t\t\t\toauthURL.searchParams.set(\"state\", encryptedPackage);\n\n\t\t\t\t\t\t\t// Update response with modified URL\n\t\t\t\t\t\t\tctx.context.returned = {\n\t\t\t\t\t\t\t\t...signInResponse,\n\t\t\t\t\t\t\t\turl: oauthURL.toString(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\t\t\t\"Failed to encrypt OAuth proxy state package:\",\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t// Continue without proxy\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn !!(\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/callback\") ||\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/oauth2/callback\")\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst headers = ctx.context.responseHeaders;\n\t\t\t\t\t\tconst location = headers?.get(\"location\");\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!location?.includes(\"/oauth-proxy-callback?callbackURL\") ||\n\t\t\t\t\t\t\t!location.startsWith(\"http\")\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst productionURL =\n\t\t\t\t\t\t\topts?.productionURL ||\n\t\t\t\t\t\t\tctx.context.options.baseURL ||\n\t\t\t\t\t\t\tctx.context.baseURL;\n\t\t\t\t\t\tconst productionOrigin = getOrigin(productionURL);\n\n\t\t\t\t\t\tconst locationURL = new URL(location);\n\t\t\t\t\t\tconst locationOrigin = locationURL.origin;\n\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Same origin: unwrap proxy redirect to original destination\n\t\t\t\t\t\t//\n\t\t\t\t\t\tif (locationOrigin === productionOrigin) {\n\t\t\t\t\t\t\tconst newLocation = locationURL.searchParams.get(\"callbackURL\");\n\t\t\t\t\t\t\tif (!newLocation) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tctx.setHeader(\"location\", newLocation);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Cross-origin: encrypt and forward cookies through proxy\n\t\t\t\t\t\t//\n\t\t\t\t\t\tconst setCookies = headers?.get(\"set-cookie\");\n\t\t\t\t\t\tif (!setCookies) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create payload with timestamp for replay attack protection\n\t\t\t\t\t\tconst payload: EncryptedCookiesPayload = {\n\t\t\t\t\t\t\tcookies: setCookies,\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst encryptedCookies = await symmetricEncrypt({\n\t\t\t\t\t\t\tkey: ctx.context.secret,\n\t\t\t\t\t\t\tdata: JSON.stringify(payload),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst locationWithCookies = `${location}&cookies=${encodeURIComponent(\n\t\t\t\t\t\t\tencryptedCookies,\n\t\t\t\t\t\t)}`;\n\n\t\t\t\t\t\tctx.setHeader(\"location\", locationWithCookies);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// Restore OAuth config after processing callback\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn !!(\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/callback\") ||\n\t\t\t\t\t\t\tcontext.path?.startsWith(\"/oauth2/callback\")\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst contextWithSnapshot = ctx.context as AuthContextWithSnapshot;\n\t\t\t\t\t\tconst snapshot = contextWithSnapshot._oauthProxySnapshot;\n\t\t\t\t\t\tif (snapshot) {\n\t\t\t\t\t\t\tctx.context.oauthConfig.storeStateStrategy =\n\t\t\t\t\t\t\t\tsnapshot.storeStateStrategy;\n\t\t\t\t\t\t\tctx.context.oauthConfig.skipStateCookieCheck =\n\t\t\t\t\t\t\t\tsnapshot.skipStateCookieCheck;\n\t\t\t\t\t\t\tctx.context.internalAdapter = snapshot.internalAdapter;\n\n\t\t\t\t\t\t\t// Clear the temporary extended context value\n\t\t\t\t\t\t\tcontextWithSnapshot._oauthProxySnapshot = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t} satisfies BetterAuthPlugin;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getOrigin } from "../../utils/url.mjs";
|
|
2
|
+
import { env } from "@better-auth/core/env";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/oauth-proxy/utils.ts
|
|
5
|
+
/**
|
|
6
|
+
* Strip trailing slashes from URL to prevent double slashes
|
|
7
|
+
*/
|
|
8
|
+
function stripTrailingSlash(url) {
|
|
9
|
+
if (!url) return "";
|
|
10
|
+
return url.replace(/\/+$/, "");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get base URL from vendor-specific environment variables
|
|
14
|
+
*/
|
|
15
|
+
function getVendorBaseURL() {
|
|
16
|
+
const vercel = env.VERCEL_URL ? `https://${env.VERCEL_URL}` : void 0;
|
|
17
|
+
const netlify = env.NETLIFY_URL;
|
|
18
|
+
const render = env.RENDER_URL;
|
|
19
|
+
const aws = env.AWS_LAMBDA_FUNCTION_NAME;
|
|
20
|
+
const google = env.GOOGLE_CLOUD_FUNCTION_NAME;
|
|
21
|
+
const azure = env.AZURE_FUNCTION_NAME;
|
|
22
|
+
return vercel || netlify || render || aws || google || azure;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the current URL from various sources
|
|
26
|
+
*/
|
|
27
|
+
function resolveCurrentURL(ctx, opts) {
|
|
28
|
+
return new URL(opts?.currentURL || ctx.request?.url || getVendorBaseURL() || ctx.context.baseURL);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if the proxy should be skipped for this request
|
|
32
|
+
*/
|
|
33
|
+
function checkSkipProxy(ctx, opts) {
|
|
34
|
+
if (ctx.request?.headers.get("x-skip-oauth-proxy")) return true;
|
|
35
|
+
const productionURL = opts?.productionURL || env.BETTER_AUTH_URL;
|
|
36
|
+
if (!productionURL) return false;
|
|
37
|
+
const currentURL = ctx.request?.url || getVendorBaseURL();
|
|
38
|
+
if (!currentURL) return false;
|
|
39
|
+
return getOrigin(productionURL) === getOrigin(currentURL);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { checkSkipProxy, resolveCurrentURL, stripTrailingSlash };
|
|
44
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/plugins/oauth-proxy/utils.ts"],"sourcesContent":["import { env } from \"@better-auth/core/env\";\nimport type { EndpointContext } from \"better-call\";\nimport { getOrigin } from \"../../utils/url\";\nimport type { OAuthProxyOptions } from \"./index\";\n\n/**\n * Strip trailing slashes from URL to prevent double slashes\n */\nexport function stripTrailingSlash(url: string | undefined): string {\n\tif (!url) return \"\";\n\treturn url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Get base URL from vendor-specific environment variables\n */\nfunction getVendorBaseURL() {\n\tconst vercel = env.VERCEL_URL ? `https://${env.VERCEL_URL}` : undefined;\n\tconst netlify = env.NETLIFY_URL;\n\tconst render = env.RENDER_URL;\n\tconst aws = env.AWS_LAMBDA_FUNCTION_NAME;\n\tconst google = env.GOOGLE_CLOUD_FUNCTION_NAME;\n\tconst azure = env.AZURE_FUNCTION_NAME;\n\n\treturn vercel || netlify || render || aws || google || azure;\n}\n\n/**\n * Resolve the current URL from various sources\n */\nexport function resolveCurrentURL(\n\tctx: EndpointContext<string, any>,\n\topts?: OAuthProxyOptions,\n) {\n\treturn new URL(\n\t\topts?.currentURL ||\n\t\t\tctx.request?.url ||\n\t\t\tgetVendorBaseURL() ||\n\t\t\tctx.context.baseURL,\n\t);\n}\n\n/**\n * Check if the proxy should be skipped for this request\n */\nexport function checkSkipProxy(\n\tctx: EndpointContext<string, any>,\n\topts?: OAuthProxyOptions,\n) {\n\t// If skip proxy header is set, we don't need to proxy\n\tconst skipProxyHeader = ctx.request?.headers.get(\"x-skip-oauth-proxy\");\n\tif (skipProxyHeader) {\n\t\treturn true;\n\t}\n\n\tconst productionURL = opts?.productionURL || env.BETTER_AUTH_URL;\n\tif (!productionURL) {\n\t\treturn false;\n\t}\n\n\tconst currentURL = ctx.request?.url || getVendorBaseURL();\n\tif (!currentURL) {\n\t\treturn false;\n\t}\n\n\tconst productionOrigin = getOrigin(productionURL);\n\tconst currentOrigin = getOrigin(currentURL);\n\n\treturn productionOrigin === currentOrigin;\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,mBAAmB,KAAiC;AACnE,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO,IAAI,QAAQ,QAAQ,GAAG;;;;;AAM/B,SAAS,mBAAmB;CAC3B,MAAM,SAAS,IAAI,aAAa,WAAW,IAAI,eAAe;CAC9D,MAAM,UAAU,IAAI;CACpB,MAAM,SAAS,IAAI;CACnB,MAAM,MAAM,IAAI;CAChB,MAAM,SAAS,IAAI;CACnB,MAAM,QAAQ,IAAI;AAElB,QAAO,UAAU,WAAW,UAAU,OAAO,UAAU;;;;;AAMxD,SAAgB,kBACf,KACA,MACC;AACD,QAAO,IAAI,IACV,MAAM,cACL,IAAI,SAAS,OACb,kBAAkB,IAClB,IAAI,QAAQ,QACb;;;;;AAMF,SAAgB,eACf,KACA,MACC;AAGD,KADwB,IAAI,SAAS,QAAQ,IAAI,qBAAqB,CAErE,QAAO;CAGR,MAAM,gBAAgB,MAAM,iBAAiB,IAAI;AACjD,KAAI,CAAC,cACJ,QAAO;CAGR,MAAM,aAAa,IAAI,SAAS,OAAO,kBAAkB;AACzD,KAAI,CAAC,WACJ,QAAO;AAMR,QAHyB,UAAU,cAAc,KAC3B,UAAU,WAAW"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
2
|
+
import "../../crypto/index.mjs";
|
|
3
|
+
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
4
|
+
import "../../api/index.mjs";
|
|
5
|
+
import { parsePrompt } from "./utils/prompt.mjs";
|
|
6
|
+
import { getClient } from "./index.mjs";
|
|
7
|
+
import { APIError } from "@better-auth/core/error";
|
|
8
|
+
|
|
9
|
+
//#region src/plugins/oidc-provider/authorize.ts
|
|
10
|
+
function formatErrorURL(url, error, description) {
|
|
11
|
+
return `${url.includes("?") ? "&" : "?"}error=${error}&error_description=${description}`;
|
|
12
|
+
}
|
|
13
|
+
function getErrorURL(ctx, error, description) {
|
|
14
|
+
return formatErrorURL(ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`, error, description);
|
|
15
|
+
}
|
|
16
|
+
async function authorize(ctx, options) {
|
|
17
|
+
const handleRedirect = (url) => {
|
|
18
|
+
if (ctx.request?.headers.get("sec-fetch-mode") === "cors") return ctx.json({
|
|
19
|
+
redirect: true,
|
|
20
|
+
url
|
|
21
|
+
});
|
|
22
|
+
else throw ctx.redirect(url);
|
|
23
|
+
};
|
|
24
|
+
const opts = {
|
|
25
|
+
codeExpiresIn: 600,
|
|
26
|
+
defaultScope: "openid",
|
|
27
|
+
...options,
|
|
28
|
+
scopes: [
|
|
29
|
+
"openid",
|
|
30
|
+
"profile",
|
|
31
|
+
"email",
|
|
32
|
+
"offline_access",
|
|
33
|
+
...options?.scopes || []
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
if (!ctx.request) throw new APIError("UNAUTHORIZED", {
|
|
37
|
+
error_description: "request not found",
|
|
38
|
+
error: "invalid_request"
|
|
39
|
+
});
|
|
40
|
+
const session = await getSessionFromCtx(ctx);
|
|
41
|
+
if (!session) {
|
|
42
|
+
const query = ctx.query;
|
|
43
|
+
if (parsePrompt(query.prompt ?? "").has("none") && query.redirect_uri) return handleRedirect(formatErrorURL(query.redirect_uri, "login_required", "Authentication required but prompt is none"));
|
|
44
|
+
/**
|
|
45
|
+
* If the user is not logged in, we need to redirect them to the
|
|
46
|
+
* login page.
|
|
47
|
+
*/
|
|
48
|
+
await ctx.setSignedCookie("oidc_login_prompt", JSON.stringify(ctx.query), ctx.context.secret, {
|
|
49
|
+
maxAge: 600,
|
|
50
|
+
path: "/",
|
|
51
|
+
sameSite: "lax"
|
|
52
|
+
});
|
|
53
|
+
const queryFromURL = ctx.request.url?.split("?")[1];
|
|
54
|
+
return handleRedirect(`${options.loginPage}?${queryFromURL}`);
|
|
55
|
+
}
|
|
56
|
+
const query = ctx.query;
|
|
57
|
+
if (!query.client_id) {
|
|
58
|
+
const errorURL = getErrorURL(ctx, "invalid_client", "client_id is required");
|
|
59
|
+
throw ctx.redirect(errorURL);
|
|
60
|
+
}
|
|
61
|
+
if (!query.response_type) {
|
|
62
|
+
const errorURL = getErrorURL(ctx, "invalid_request", "response_type is required");
|
|
63
|
+
throw ctx.redirect(errorURL);
|
|
64
|
+
}
|
|
65
|
+
const client = await getClient(ctx.query.client_id, options.trustedClients || []);
|
|
66
|
+
if (!client) {
|
|
67
|
+
const errorURL = getErrorURL(ctx, "invalid_client", "client_id is required");
|
|
68
|
+
throw ctx.redirect(errorURL);
|
|
69
|
+
}
|
|
70
|
+
const redirectURI = client.redirectUrls.find((url) => url === ctx.query.redirect_uri);
|
|
71
|
+
if (!redirectURI || !query.redirect_uri)
|
|
72
|
+
/**
|
|
73
|
+
* show UI error here warning the user that the redirect URI is invalid
|
|
74
|
+
*/
|
|
75
|
+
throw new APIError("BAD_REQUEST", { message: "Invalid redirect URI" });
|
|
76
|
+
if (client.disabled) {
|
|
77
|
+
const errorURL = getErrorURL(ctx, "client_disabled", "client is disabled");
|
|
78
|
+
throw ctx.redirect(errorURL);
|
|
79
|
+
}
|
|
80
|
+
if (query.response_type !== "code") {
|
|
81
|
+
const errorURL = getErrorURL(ctx, "unsupported_response_type", "unsupported response type");
|
|
82
|
+
throw ctx.redirect(errorURL);
|
|
83
|
+
}
|
|
84
|
+
const requestScope = query.scope?.split(" ").filter((s) => s) || opts.defaultScope?.split(" ") || [];
|
|
85
|
+
const invalidScopes = requestScope.filter((scope) => {
|
|
86
|
+
return !opts.scopes.includes(scope);
|
|
87
|
+
});
|
|
88
|
+
if (invalidScopes.length) return handleRedirect(formatErrorURL(query.redirect_uri, "invalid_scope", `The following scopes are invalid: ${invalidScopes.join(", ")}`));
|
|
89
|
+
if ((!query.code_challenge || !query.code_challenge_method) && options.requirePKCE) return handleRedirect(formatErrorURL(query.redirect_uri, "invalid_request", "pkce is required"));
|
|
90
|
+
if (!query.code_challenge_method) query.code_challenge_method = "plain";
|
|
91
|
+
if (!["s256", options.allowPlainCodeChallengeMethod ? "plain" : "s256"].includes(query.code_challenge_method?.toLowerCase() || "")) return handleRedirect(formatErrorURL(query.redirect_uri, "invalid_request", "invalid code_challenge method"));
|
|
92
|
+
const code = generateRandomString(32, "a-z", "A-Z", "0-9");
|
|
93
|
+
const codeExpiresInMs = opts.codeExpiresIn * 1e3;
|
|
94
|
+
const expiresAt = new Date(Date.now() + codeExpiresInMs);
|
|
95
|
+
const skipConsentForTrustedClient = client.skipConsent;
|
|
96
|
+
const hasAlreadyConsented = await ctx.context.adapter.findOne({
|
|
97
|
+
model: "oauthConsent",
|
|
98
|
+
where: [{
|
|
99
|
+
field: "clientId",
|
|
100
|
+
value: client.clientId
|
|
101
|
+
}, {
|
|
102
|
+
field: "userId",
|
|
103
|
+
value: session.user.id
|
|
104
|
+
}]
|
|
105
|
+
}).then((res) => {
|
|
106
|
+
if (!res?.consentGiven) return false;
|
|
107
|
+
const consentedScopes = res.scopes ? res.scopes.split(" ") : [];
|
|
108
|
+
return requestScope.every((scope) => consentedScopes.includes(scope));
|
|
109
|
+
});
|
|
110
|
+
const promptSet = parsePrompt(query.prompt ?? "");
|
|
111
|
+
if (promptSet.has("none")) {
|
|
112
|
+
if (!skipConsentForTrustedClient && !hasAlreadyConsented) return handleRedirect(formatErrorURL(query.redirect_uri, "consent_required", "Consent required but prompt is none"));
|
|
113
|
+
}
|
|
114
|
+
let requireLogin = promptSet.has("login");
|
|
115
|
+
if (query.max_age !== void 0) {
|
|
116
|
+
const maxAge = Number(query.max_age);
|
|
117
|
+
if (Number.isInteger(maxAge) && maxAge >= 0) {
|
|
118
|
+
if ((Date.now() - new Date(session.session.createdAt).getTime()) / 1e3 > maxAge) requireLogin = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const requireConsent = !skipConsentForTrustedClient && (!hasAlreadyConsented || promptSet.has("consent"));
|
|
122
|
+
try {
|
|
123
|
+
/**
|
|
124
|
+
* Save the code in the database
|
|
125
|
+
*/
|
|
126
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
127
|
+
value: JSON.stringify({
|
|
128
|
+
clientId: client.clientId,
|
|
129
|
+
redirectURI: query.redirect_uri,
|
|
130
|
+
scope: requestScope,
|
|
131
|
+
userId: session.user.id,
|
|
132
|
+
authTime: new Date(session.session.createdAt).getTime(),
|
|
133
|
+
requireConsent,
|
|
134
|
+
state: requireConsent ? query.state : null,
|
|
135
|
+
codeChallenge: query.code_challenge,
|
|
136
|
+
codeChallengeMethod: query.code_challenge_method,
|
|
137
|
+
nonce: query.nonce
|
|
138
|
+
}),
|
|
139
|
+
identifier: code,
|
|
140
|
+
expiresAt
|
|
141
|
+
});
|
|
142
|
+
} catch {
|
|
143
|
+
return handleRedirect(formatErrorURL(query.redirect_uri, "server_error", "An error occurred while processing the request"));
|
|
144
|
+
}
|
|
145
|
+
if (requireLogin) {
|
|
146
|
+
await ctx.setSignedCookie("oidc_login_prompt", JSON.stringify(ctx.query), ctx.context.secret, {
|
|
147
|
+
maxAge: 600,
|
|
148
|
+
path: "/",
|
|
149
|
+
sameSite: "lax"
|
|
150
|
+
});
|
|
151
|
+
await ctx.setSignedCookie("oidc_consent_prompt", code, ctx.context.secret, {
|
|
152
|
+
maxAge: 600,
|
|
153
|
+
path: "/",
|
|
154
|
+
sameSite: "lax"
|
|
155
|
+
});
|
|
156
|
+
return handleRedirect(`${options.loginPage}?${new URLSearchParams({
|
|
157
|
+
client_id: client.clientId,
|
|
158
|
+
code,
|
|
159
|
+
state: query.state
|
|
160
|
+
}).toString()}`);
|
|
161
|
+
}
|
|
162
|
+
if (!requireConsent) {
|
|
163
|
+
const redirectURIWithCode = new URL(redirectURI);
|
|
164
|
+
redirectURIWithCode.searchParams.set("code", code);
|
|
165
|
+
redirectURIWithCode.searchParams.set("state", ctx.query.state);
|
|
166
|
+
return handleRedirect(redirectURIWithCode.toString());
|
|
167
|
+
}
|
|
168
|
+
if (options?.consentPage) {
|
|
169
|
+
await ctx.setSignedCookie("oidc_consent_prompt", code, ctx.context.secret, {
|
|
170
|
+
maxAge: 600,
|
|
171
|
+
path: "/",
|
|
172
|
+
sameSite: "lax"
|
|
173
|
+
});
|
|
174
|
+
const urlParams = new URLSearchParams();
|
|
175
|
+
urlParams.set("consent_code", code);
|
|
176
|
+
urlParams.set("client_id", client.clientId);
|
|
177
|
+
urlParams.set("scope", requestScope.join(" "));
|
|
178
|
+
return handleRedirect(`${options.consentPage}?${urlParams.toString()}`);
|
|
179
|
+
}
|
|
180
|
+
const htmlFn = options?.getConsentHTML;
|
|
181
|
+
if (!htmlFn) throw new APIError("INTERNAL_SERVER_ERROR", { message: "No consent page provided" });
|
|
182
|
+
return new Response(htmlFn({
|
|
183
|
+
scopes: requestScope,
|
|
184
|
+
clientMetadata: client.metadata,
|
|
185
|
+
clientIcon: client?.icon,
|
|
186
|
+
clientId: client.clientId,
|
|
187
|
+
clientName: client.name,
|
|
188
|
+
code
|
|
189
|
+
}), { headers: { "content-type": "text/html" } });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
export { authorize };
|
|
194
|
+
//# sourceMappingURL=authorize.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.mjs","names":[],"sources":["../../../src/plugins/oidc-provider/authorize.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { getSessionFromCtx } from \"../../api\";\nimport { generateRandomString } from \"../../crypto\";\nimport { getClient } from \"./index\";\nimport type { AuthorizationQuery, OIDCOptions } from \"./types\";\nimport { parsePrompt } from \"./utils/prompt\";\n\nfunction formatErrorURL(url: string, error: string, description: string) {\n\treturn `${\n\t\turl.includes(\"?\") ? \"&\" : \"?\"\n\t}error=${error}&error_description=${description}`;\n}\n\nfunction getErrorURL(\n\tctx: GenericEndpointContext,\n\terror: string,\n\tdescription: string,\n) {\n\tconst baseURL =\n\t\tctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;\n\tconst formattedURL = formatErrorURL(baseURL, error, description);\n\treturn formattedURL;\n}\n\nexport async function authorize(\n\tctx: GenericEndpointContext,\n\toptions: OIDCOptions,\n) {\n\tconst handleRedirect = (url: string) => {\n\t\tconst fromFetch = ctx.request?.headers.get(\"sec-fetch-mode\") === \"cors\";\n\t\tif (fromFetch) {\n\t\t\treturn ctx.json({\n\t\t\t\tredirect: true,\n\t\t\t\turl,\n\t\t\t});\n\t\t} else {\n\t\t\tthrow ctx.redirect(url);\n\t\t}\n\t};\n\n\tconst opts = {\n\t\tcodeExpiresIn: 600,\n\t\tdefaultScope: \"openid\",\n\t\t...options,\n\t\tscopes: [\n\t\t\t\"openid\",\n\t\t\t\"profile\",\n\t\t\t\"email\",\n\t\t\t\"offline_access\",\n\t\t\t...(options?.scopes || []),\n\t\t],\n\t};\n\tif (!ctx.request) {\n\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\terror_description: \"request not found\",\n\t\t\terror: \"invalid_request\",\n\t\t});\n\t}\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session) {\n\t\t// Handle prompt=none per OIDC spec - must return error instead of redirecting\n\t\tconst query = ctx.query as AuthorizationQuery;\n\t\tconst promptSet = parsePrompt(query.prompt ?? \"\");\n\t\tif (promptSet.has(\"none\") && query.redirect_uri) {\n\t\t\treturn handleRedirect(\n\t\t\t\tformatErrorURL(\n\t\t\t\t\tquery.redirect_uri,\n\t\t\t\t\t\"login_required\",\n\t\t\t\t\t\"Authentication required but prompt is none\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t/**\n\t\t * If the user is not logged in, we need to redirect them to the\n\t\t * login page.\n\t\t */\n\t\tawait ctx.setSignedCookie(\n\t\t\t\"oidc_login_prompt\",\n\t\t\tJSON.stringify(ctx.query),\n\t\t\tctx.context.secret,\n\t\t\t{\n\t\t\t\tmaxAge: 600,\n\t\t\t\tpath: \"/\",\n\t\t\t\tsameSite: \"lax\",\n\t\t\t},\n\t\t);\n\t\tconst queryFromURL = ctx.request.url?.split(\"?\")[1]!;\n\t\treturn handleRedirect(`${options.loginPage}?${queryFromURL}`);\n\t}\n\n\tconst query = ctx.query as AuthorizationQuery;\n\tif (!query.client_id) {\n\t\tconst errorURL = getErrorURL(\n\t\t\tctx,\n\t\t\t\"invalid_client\",\n\t\t\t\"client_id is required\",\n\t\t);\n\t\tthrow ctx.redirect(errorURL);\n\t}\n\n\tif (!query.response_type) {\n\t\tconst errorURL = getErrorURL(\n\t\t\tctx,\n\t\t\t\"invalid_request\",\n\t\t\t\"response_type is required\",\n\t\t);\n\t\tthrow ctx.redirect(errorURL);\n\t}\n\n\tconst client = await getClient(\n\t\tctx.query.client_id,\n\t\toptions.trustedClients || [],\n\t);\n\tif (!client) {\n\t\tconst errorURL = getErrorURL(\n\t\t\tctx,\n\t\t\t\"invalid_client\",\n\t\t\t\"client_id is required\",\n\t\t);\n\t\tthrow ctx.redirect(errorURL);\n\t}\n\tconst redirectURI = client.redirectUrls.find(\n\t\t(url) => url === ctx.query.redirect_uri,\n\t);\n\n\tif (!redirectURI || !query.redirect_uri) {\n\t\t/**\n\t\t * show UI error here warning the user that the redirect URI is invalid\n\t\t */\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"Invalid redirect URI\",\n\t\t});\n\t}\n\tif (client.disabled) {\n\t\tconst errorURL = getErrorURL(ctx, \"client_disabled\", \"client is disabled\");\n\t\tthrow ctx.redirect(errorURL);\n\t}\n\n\tif (query.response_type !== \"code\") {\n\t\tconst errorURL = getErrorURL(\n\t\t\tctx,\n\t\t\t\"unsupported_response_type\",\n\t\t\t\"unsupported response type\",\n\t\t);\n\t\tthrow ctx.redirect(errorURL);\n\t}\n\n\tconst requestScope =\n\t\tquery.scope?.split(\" \").filter((s) => s) ||\n\t\topts.defaultScope?.split(\" \") ||\n\t\t[];\n\tconst invalidScopes = requestScope.filter((scope) => {\n\t\treturn !opts.scopes.includes(scope);\n\t});\n\tif (invalidScopes.length) {\n\t\treturn handleRedirect(\n\t\t\tformatErrorURL(\n\t\t\t\tquery.redirect_uri,\n\t\t\t\t\"invalid_scope\",\n\t\t\t\t`The following scopes are invalid: ${invalidScopes.join(\", \")}`,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\n\t\t(!query.code_challenge || !query.code_challenge_method) &&\n\t\toptions.requirePKCE\n\t) {\n\t\treturn handleRedirect(\n\t\t\tformatErrorURL(query.redirect_uri, \"invalid_request\", \"pkce is required\"),\n\t\t);\n\t}\n\n\tif (!query.code_challenge_method) {\n\t\tquery.code_challenge_method = \"plain\";\n\t}\n\n\tif (\n\t\t![\n\t\t\t\"s256\",\n\t\t\toptions.allowPlainCodeChallengeMethod ? \"plain\" : \"s256\",\n\t\t].includes(query.code_challenge_method?.toLowerCase() || \"\")\n\t) {\n\t\treturn handleRedirect(\n\t\t\tformatErrorURL(\n\t\t\t\tquery.redirect_uri,\n\t\t\t\t\"invalid_request\",\n\t\t\t\t\"invalid code_challenge method\",\n\t\t\t),\n\t\t);\n\t}\n\n\tconst code = generateRandomString(32, \"a-z\", \"A-Z\", \"0-9\");\n\tconst codeExpiresInMs = opts.codeExpiresIn! * 1000;\n\tconst expiresAt = new Date(Date.now() + codeExpiresInMs);\n\n\t// Determine if consent is required\n\t// Consent is ALWAYS required unless:\n\t// 1. The client is trusted (skipConsent = true)\n\t// 2. The user has already consented and prompt is not \"consent\"\n\tconst skipConsentForTrustedClient = client.skipConsent;\n\tconst hasAlreadyConsented = await ctx.context.adapter\n\t\t.findOne<{\n\t\t\tconsentGiven: boolean;\n\t\t\tscopes: string;\n\t\t}>({\n\t\t\tmodel: \"oauthConsent\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"clientId\",\n\t\t\t\t\tvalue: client.clientId,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\tvalue: session.user.id,\n\t\t\t\t},\n\t\t\t],\n\t\t})\n\t\t.then((res) => {\n\t\t\tif (!res?.consentGiven) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst consentedScopes = res.scopes ? res.scopes.split(\" \") : [];\n\t\t\tconst hasConsented = requestScope.every((scope) =>\n\t\t\t\tconsentedScopes.includes(scope),\n\t\t\t);\n\t\t\treturn hasConsented;\n\t\t});\n\n\tconst promptSet = parsePrompt(query.prompt ?? \"\");\n\n\t// Handle prompt=none per OIDC spec 3.1.2.1\n\t// The Authorization Server MUST NOT display any authentication or consent UI\n\tif (promptSet.has(\"none\")) {\n\t\t// If consent is required, return consent_required error\n\t\tif (!skipConsentForTrustedClient && !hasAlreadyConsented) {\n\t\t\treturn handleRedirect(\n\t\t\t\tformatErrorURL(\n\t\t\t\t\tquery.redirect_uri,\n\t\t\t\t\t\"consent_required\",\n\t\t\t\t\t\"Consent required but prompt is none\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\t// If we reach here, user is authenticated and consent is satisfied\n\t\t// Continue without any UI interaction\n\t}\n\n\t// Handle max_age parameter per OIDC spec 3.1.2.1\n\t// max_age=0 is equivalent to prompt=login\n\tlet requireLogin = promptSet.has(\"login\");\n\tif (query.max_age !== undefined) {\n\t\tconst maxAge = Number(query.max_age);\n\t\tif (Number.isInteger(maxAge) && maxAge >= 0) {\n\t\t\tconst sessionAge =\n\t\t\t\t(Date.now() - new Date(session.session.createdAt).getTime()) / 1000;\n\t\t\tif (sessionAge > maxAge) {\n\t\t\t\t// Session is older than max_age, force re-authentication\n\t\t\t\trequireLogin = true;\n\t\t\t}\n\t\t}\n\t\t// If max_age is invalid (not a non-negative integer), ignore it per OIDC spec\n\t}\n\n\tconst requireConsent =\n\t\t!skipConsentForTrustedClient &&\n\t\t(!hasAlreadyConsented || promptSet.has(\"consent\"));\n\n\ttry {\n\t\t/**\n\t\t * Save the code in the database\n\t\t */\n\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\tvalue: JSON.stringify({\n\t\t\t\tclientId: client.clientId,\n\t\t\t\tredirectURI: query.redirect_uri,\n\t\t\t\tscope: requestScope,\n\t\t\t\tuserId: session.user.id,\n\t\t\t\tauthTime: new Date(session.session.createdAt).getTime(),\n\t\t\t\t/**\n\t\t\t\t * Consent is required per OIDC spec unless:\n\t\t\t\t * 1. Client is trusted (skipConsent = true)\n\t\t\t\t * 2. User has already consented (and prompt is not \"consent\")\n\t\t\t\t *\n\t\t\t\t * When consent is required, the code needs to be treated as a\n\t\t\t\t * consent request. Once the user consents, the code will be\n\t\t\t\t * updated with the actual authorization code.\n\t\t\t\t */\n\t\t\t\trequireConsent,\n\t\t\t\tstate: requireConsent ? query.state : null,\n\t\t\t\tcodeChallenge: query.code_challenge,\n\t\t\t\tcodeChallengeMethod: query.code_challenge_method,\n\t\t\t\tnonce: query.nonce,\n\t\t\t}),\n\t\t\tidentifier: code,\n\t\t\texpiresAt,\n\t\t});\n\t} catch {\n\t\treturn handleRedirect(\n\t\t\tformatErrorURL(\n\t\t\t\tquery.redirect_uri,\n\t\t\t\t\"server_error\",\n\t\t\t\t\"An error occurred while processing the request\",\n\t\t\t),\n\t\t);\n\t}\n\n\tif (requireLogin) {\n\t\tawait ctx.setSignedCookie(\n\t\t\t\"oidc_login_prompt\",\n\t\t\tJSON.stringify(ctx.query),\n\t\t\tctx.context.secret,\n\t\t\t{\n\t\t\t\tmaxAge: 600,\n\t\t\t\tpath: \"/\",\n\t\t\t\tsameSite: \"lax\",\n\t\t\t},\n\t\t);\n\t\tawait ctx.setSignedCookie(\"oidc_consent_prompt\", code, ctx.context.secret, {\n\t\t\tmaxAge: 600,\n\t\t\tpath: \"/\",\n\t\t\tsameSite: \"lax\",\n\t\t});\n\n\t\tconst loginURI = `${options.loginPage}?${new URLSearchParams({\n\t\t\tclient_id: client.clientId,\n\t\t\tcode,\n\t\t\tstate: query.state,\n\t\t}).toString()}`;\n\t\treturn handleRedirect(loginURI);\n\t}\n\n\t// If consent is not required, redirect with the code immediately\n\tif (!requireConsent) {\n\t\tconst redirectURIWithCode = new URL(redirectURI);\n\t\tredirectURIWithCode.searchParams.set(\"code\", code);\n\t\tredirectURIWithCode.searchParams.set(\"state\", ctx.query.state);\n\t\treturn handleRedirect(redirectURIWithCode.toString());\n\t}\n\n\t// Consent is required - redirect to consent page or show consent HTML\n\n\tif (options?.consentPage) {\n\t\t// Set cookie to support cookie-based consent flows\n\t\tawait ctx.setSignedCookie(\"oidc_consent_prompt\", code, ctx.context.secret, {\n\t\t\tmaxAge: 600,\n\t\t\tpath: \"/\",\n\t\t\tsameSite: \"lax\",\n\t\t});\n\n\t\t// Pass the consent code as a URL parameter to support URL-based consent flows\n\t\tconst urlParams = new URLSearchParams();\n\t\turlParams.set(\"consent_code\", code);\n\t\turlParams.set(\"client_id\", client.clientId);\n\t\turlParams.set(\"scope\", requestScope.join(\" \"));\n\t\tconst consentURI = `${options.consentPage}?${urlParams.toString()}`;\n\n\t\treturn handleRedirect(consentURI);\n\t}\n\tconst htmlFn = options?.getConsentHTML;\n\n\tif (!htmlFn) {\n\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\tmessage: \"No consent page provided\",\n\t\t});\n\t}\n\n\treturn new Response(\n\t\thtmlFn({\n\t\t\tscopes: requestScope,\n\t\t\tclientMetadata: client.metadata,\n\t\t\tclientIcon: client?.icon,\n\t\t\tclientId: client.clientId,\n\t\t\tclientName: client.name,\n\t\t\tcode,\n\t\t}),\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"text/html\",\n\t\t\t},\n\t\t},\n\t);\n}\n"],"mappings":";;;;;;;;;AAQA,SAAS,eAAe,KAAa,OAAe,aAAqB;AACxE,QAAO,GACN,IAAI,SAAS,IAAI,GAAG,MAAM,IAC1B,QAAQ,MAAM,qBAAqB;;AAGrC,SAAS,YACR,KACA,OACA,aACC;AAID,QADqB,eADpB,IAAI,QAAQ,QAAQ,YAAY,YAAY,GAAG,IAAI,QAAQ,QAAQ,SACvB,OAAO,YAAY;;AAIjE,eAAsB,UACrB,KACA,SACC;CACD,MAAM,kBAAkB,QAAgB;AAEvC,MADkB,IAAI,SAAS,QAAQ,IAAI,iBAAiB,KAAK,OAEhE,QAAO,IAAI,KAAK;GACf,UAAU;GACV;GACA,CAAC;MAEF,OAAM,IAAI,SAAS,IAAI;;CAIzB,MAAM,OAAO;EACZ,eAAe;EACf,cAAc;EACd,GAAG;EACH,QAAQ;GACP;GACA;GACA;GACA;GACA,GAAI,SAAS,UAAU,EAAE;GACzB;EACD;AACD,KAAI,CAAC,IAAI,QACR,OAAM,IAAI,SAAS,gBAAgB;EAClC,mBAAmB;EACnB,OAAO;EACP,CAAC;CAEH,MAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,KAAI,CAAC,SAAS;EAEb,MAAM,QAAQ,IAAI;AAElB,MADkB,YAAY,MAAM,UAAU,GAAG,CACnC,IAAI,OAAO,IAAI,MAAM,aAClC,QAAO,eACN,eACC,MAAM,cACN,kBACA,6CACA,CACD;;;;;AAOF,QAAM,IAAI,gBACT,qBACA,KAAK,UAAU,IAAI,MAAM,EACzB,IAAI,QAAQ,QACZ;GACC,QAAQ;GACR,MAAM;GACN,UAAU;GACV,CACD;EACD,MAAM,eAAe,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC;AACjD,SAAO,eAAe,GAAG,QAAQ,UAAU,GAAG,eAAe;;CAG9D,MAAM,QAAQ,IAAI;AAClB,KAAI,CAAC,MAAM,WAAW;EACrB,MAAM,WAAW,YAChB,KACA,kBACA,wBACA;AACD,QAAM,IAAI,SAAS,SAAS;;AAG7B,KAAI,CAAC,MAAM,eAAe;EACzB,MAAM,WAAW,YAChB,KACA,mBACA,4BACA;AACD,QAAM,IAAI,SAAS,SAAS;;CAG7B,MAAM,SAAS,MAAM,UACpB,IAAI,MAAM,WACV,QAAQ,kBAAkB,EAAE,CAC5B;AACD,KAAI,CAAC,QAAQ;EACZ,MAAM,WAAW,YAChB,KACA,kBACA,wBACA;AACD,QAAM,IAAI,SAAS,SAAS;;CAE7B,MAAM,cAAc,OAAO,aAAa,MACtC,QAAQ,QAAQ,IAAI,MAAM,aAC3B;AAED,KAAI,CAAC,eAAe,CAAC,MAAM;;;;AAI1B,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,wBACT,CAAC;AAEH,KAAI,OAAO,UAAU;EACpB,MAAM,WAAW,YAAY,KAAK,mBAAmB,qBAAqB;AAC1E,QAAM,IAAI,SAAS,SAAS;;AAG7B,KAAI,MAAM,kBAAkB,QAAQ;EACnC,MAAM,WAAW,YAChB,KACA,6BACA,4BACA;AACD,QAAM,IAAI,SAAS,SAAS;;CAG7B,MAAM,eACL,MAAM,OAAO,MAAM,IAAI,CAAC,QAAQ,MAAM,EAAE,IACxC,KAAK,cAAc,MAAM,IAAI,IAC7B,EAAE;CACH,MAAM,gBAAgB,aAAa,QAAQ,UAAU;AACpD,SAAO,CAAC,KAAK,OAAO,SAAS,MAAM;GAClC;AACF,KAAI,cAAc,OACjB,QAAO,eACN,eACC,MAAM,cACN,iBACA,qCAAqC,cAAc,KAAK,KAAK,GAC7D,CACD;AAGF,MACE,CAAC,MAAM,kBAAkB,CAAC,MAAM,0BACjC,QAAQ,YAER,QAAO,eACN,eAAe,MAAM,cAAc,mBAAmB,mBAAmB,CACzE;AAGF,KAAI,CAAC,MAAM,sBACV,OAAM,wBAAwB;AAG/B,KACC,CAAC,CACA,QACA,QAAQ,gCAAgC,UAAU,OAClD,CAAC,SAAS,MAAM,uBAAuB,aAAa,IAAI,GAAG,CAE5D,QAAO,eACN,eACC,MAAM,cACN,mBACA,gCACA,CACD;CAGF,MAAM,OAAO,qBAAqB,IAAI,OAAO,OAAO,MAAM;CAC1D,MAAM,kBAAkB,KAAK,gBAAiB;CAC9C,MAAM,YAAY,IAAI,KAAK,KAAK,KAAK,GAAG,gBAAgB;CAMxD,MAAM,8BAA8B,OAAO;CAC3C,MAAM,sBAAsB,MAAM,IAAI,QAAQ,QAC5C,QAGE;EACF,OAAO;EACP,OAAO,CACN;GACC,OAAO;GACP,OAAO,OAAO;GACd,EACD;GACC,OAAO;GACP,OAAO,QAAQ,KAAK;GACpB,CACD;EACD,CAAC,CACD,MAAM,QAAQ;AACd,MAAI,CAAC,KAAK,aACT,QAAO;EAER,MAAM,kBAAkB,IAAI,SAAS,IAAI,OAAO,MAAM,IAAI,GAAG,EAAE;AAI/D,SAHqB,aAAa,OAAO,UACxC,gBAAgB,SAAS,MAAM,CAC/B;GAEA;CAEH,MAAM,YAAY,YAAY,MAAM,UAAU,GAAG;AAIjD,KAAI,UAAU,IAAI,OAAO,EAExB;MAAI,CAAC,+BAA+B,CAAC,oBACpC,QAAO,eACN,eACC,MAAM,cACN,oBACA,sCACA,CACD;;CAQH,IAAI,eAAe,UAAU,IAAI,QAAQ;AACzC,KAAI,MAAM,YAAY,QAAW;EAChC,MAAM,SAAS,OAAO,MAAM,QAAQ;AACpC,MAAI,OAAO,UAAU,OAAO,IAAI,UAAU,GAGzC;QADE,KAAK,KAAK,GAAG,IAAI,KAAK,QAAQ,QAAQ,UAAU,CAAC,SAAS,IAAI,MAC/C,OAEhB,gBAAe;;;CAMlB,MAAM,iBACL,CAAC,gCACA,CAAC,uBAAuB,UAAU,IAAI,UAAU;AAElD,KAAI;;;;AAIH,QAAM,IAAI,QAAQ,gBAAgB,wBAAwB;GACzD,OAAO,KAAK,UAAU;IACrB,UAAU,OAAO;IACjB,aAAa,MAAM;IACnB,OAAO;IACP,QAAQ,QAAQ,KAAK;IACrB,UAAU,IAAI,KAAK,QAAQ,QAAQ,UAAU,CAAC,SAAS;IAUvD;IACA,OAAO,iBAAiB,MAAM,QAAQ;IACtC,eAAe,MAAM;IACrB,qBAAqB,MAAM;IAC3B,OAAO,MAAM;IACb,CAAC;GACF,YAAY;GACZ;GACA,CAAC;SACK;AACP,SAAO,eACN,eACC,MAAM,cACN,gBACA,iDACA,CACD;;AAGF,KAAI,cAAc;AACjB,QAAM,IAAI,gBACT,qBACA,KAAK,UAAU,IAAI,MAAM,EACzB,IAAI,QAAQ,QACZ;GACC,QAAQ;GACR,MAAM;GACN,UAAU;GACV,CACD;AACD,QAAM,IAAI,gBAAgB,uBAAuB,MAAM,IAAI,QAAQ,QAAQ;GAC1E,QAAQ;GACR,MAAM;GACN,UAAU;GACV,CAAC;AAOF,SAAO,eALU,GAAG,QAAQ,UAAU,GAAG,IAAI,gBAAgB;GAC5D,WAAW,OAAO;GAClB;GACA,OAAO,MAAM;GACb,CAAC,CAAC,UAAU,GACkB;;AAIhC,KAAI,CAAC,gBAAgB;EACpB,MAAM,sBAAsB,IAAI,IAAI,YAAY;AAChD,sBAAoB,aAAa,IAAI,QAAQ,KAAK;AAClD,sBAAoB,aAAa,IAAI,SAAS,IAAI,MAAM,MAAM;AAC9D,SAAO,eAAe,oBAAoB,UAAU,CAAC;;AAKtD,KAAI,SAAS,aAAa;AAEzB,QAAM,IAAI,gBAAgB,uBAAuB,MAAM,IAAI,QAAQ,QAAQ;GAC1E,QAAQ;GACR,MAAM;GACN,UAAU;GACV,CAAC;EAGF,MAAM,YAAY,IAAI,iBAAiB;AACvC,YAAU,IAAI,gBAAgB,KAAK;AACnC,YAAU,IAAI,aAAa,OAAO,SAAS;AAC3C,YAAU,IAAI,SAAS,aAAa,KAAK,IAAI,CAAC;AAG9C,SAAO,eAFY,GAAG,QAAQ,YAAY,GAAG,UAAU,UAAU,GAEhC;;CAElC,MAAM,SAAS,SAAS;AAExB,KAAI,CAAC,OACJ,OAAM,IAAI,SAAS,yBAAyB,EAC3C,SAAS,4BACT,CAAC;AAGH,QAAO,IAAI,SACV,OAAO;EACN,QAAQ;EACR,gBAAgB,OAAO;EACvB,YAAY,QAAQ;EACpB,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB;EACA,CAAC,EACF,EACC,SAAS,EACR,gBAAgB,aAChB,EACD,CACD"}
|