@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,1093 @@
|
|
|
1
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
2
|
+
import { symmetricDecrypt, symmetricEncrypt } from "../../crypto/index.mjs";
|
|
3
|
+
import { mergeSchema } from "../../db/schema.mjs";
|
|
4
|
+
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
5
|
+
import { expireCookie } from "../../cookies/index.mjs";
|
|
6
|
+
import "../../db/index.mjs";
|
|
7
|
+
import { getSessionFromCtx, sessionMiddleware } from "../../api/routes/session.mjs";
|
|
8
|
+
import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
|
|
9
|
+
import "../../utils/index.mjs";
|
|
10
|
+
import { APIError } from "../../api/index.mjs";
|
|
11
|
+
import { getJwtToken } from "../jwt/sign.mjs";
|
|
12
|
+
import { verifyJWT } from "../jwt/verify.mjs";
|
|
13
|
+
import "../jwt/index.mjs";
|
|
14
|
+
import { parsePrompt } from "./utils/prompt.mjs";
|
|
15
|
+
import { authorize } from "./authorize.mjs";
|
|
16
|
+
import { schema } from "./schema.mjs";
|
|
17
|
+
import { defaultClientSecretHasher } from "./utils.mjs";
|
|
18
|
+
import { getCurrentAuthContext } from "@better-auth/core/context";
|
|
19
|
+
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
20
|
+
import * as z from "zod";
|
|
21
|
+
import { createHash } from "@better-auth/utils/hash";
|
|
22
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
23
|
+
import { base64 } from "@better-auth/utils/base64";
|
|
24
|
+
|
|
25
|
+
//#region src/plugins/oidc-provider/index.ts
|
|
26
|
+
/**
|
|
27
|
+
* Get a client by ID, checking trusted clients first, then database
|
|
28
|
+
*/
|
|
29
|
+
async function getClient(clientId, trustedClients = []) {
|
|
30
|
+
const { context: { adapter } } = await getCurrentAuthContext();
|
|
31
|
+
const trustedClient = trustedClients.find((client) => client.clientId === clientId);
|
|
32
|
+
if (trustedClient) return trustedClient;
|
|
33
|
+
return adapter.findOne({
|
|
34
|
+
model: "oauthApplication",
|
|
35
|
+
where: [{
|
|
36
|
+
field: "clientId",
|
|
37
|
+
value: clientId
|
|
38
|
+
}]
|
|
39
|
+
}).then((res) => {
|
|
40
|
+
if (!res) return null;
|
|
41
|
+
return {
|
|
42
|
+
clientId: res.clientId,
|
|
43
|
+
clientSecret: res.clientSecret,
|
|
44
|
+
type: res.type,
|
|
45
|
+
name: res.name,
|
|
46
|
+
icon: res.icon,
|
|
47
|
+
disabled: res.disabled,
|
|
48
|
+
redirectUrls: (res.redirectUrls ?? "").split(","),
|
|
49
|
+
metadata: res.metadata ? JSON.parse(res.metadata) : {}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const getMetadata = (ctx, options) => {
|
|
54
|
+
const jwtPlugin = ctx.context.getPlugin("jwt");
|
|
55
|
+
const issuer = jwtPlugin && jwtPlugin.options?.jwt && jwtPlugin.options.jwt.issuer ? jwtPlugin.options.jwt.issuer : ctx.context.options.baseURL;
|
|
56
|
+
const baseURL = ctx.context.baseURL;
|
|
57
|
+
const supportedAlgs = options?.useJWTPlugin ? [
|
|
58
|
+
"RS256",
|
|
59
|
+
"EdDSA",
|
|
60
|
+
"none"
|
|
61
|
+
] : ["HS256", "none"];
|
|
62
|
+
return {
|
|
63
|
+
issuer,
|
|
64
|
+
authorization_endpoint: `${baseURL}/oauth2/authorize`,
|
|
65
|
+
token_endpoint: `${baseURL}/oauth2/token`,
|
|
66
|
+
userinfo_endpoint: `${baseURL}/oauth2/userinfo`,
|
|
67
|
+
jwks_uri: `${baseURL}/jwks`,
|
|
68
|
+
registration_endpoint: `${baseURL}/oauth2/register`,
|
|
69
|
+
end_session_endpoint: `${baseURL}/oauth2/endsession`,
|
|
70
|
+
scopes_supported: [
|
|
71
|
+
"openid",
|
|
72
|
+
"profile",
|
|
73
|
+
"email",
|
|
74
|
+
"offline_access"
|
|
75
|
+
],
|
|
76
|
+
response_types_supported: ["code"],
|
|
77
|
+
response_modes_supported: ["query"],
|
|
78
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
79
|
+
acr_values_supported: ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"],
|
|
80
|
+
subject_types_supported: ["public"],
|
|
81
|
+
id_token_signing_alg_values_supported: supportedAlgs,
|
|
82
|
+
token_endpoint_auth_methods_supported: [
|
|
83
|
+
"client_secret_basic",
|
|
84
|
+
"client_secret_post",
|
|
85
|
+
"none"
|
|
86
|
+
],
|
|
87
|
+
code_challenge_methods_supported: ["S256"],
|
|
88
|
+
claims_supported: [
|
|
89
|
+
"sub",
|
|
90
|
+
"iss",
|
|
91
|
+
"aud",
|
|
92
|
+
"exp",
|
|
93
|
+
"nbf",
|
|
94
|
+
"iat",
|
|
95
|
+
"jti",
|
|
96
|
+
"email",
|
|
97
|
+
"email_verified",
|
|
98
|
+
"name"
|
|
99
|
+
],
|
|
100
|
+
...options?.metadata
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
const oAuthConsentBodySchema = z.object({
|
|
104
|
+
accept: z.boolean(),
|
|
105
|
+
consent_code: z.string().optional().nullish()
|
|
106
|
+
});
|
|
107
|
+
const oAuth2TokenBodySchema = z.record(z.any(), z.any());
|
|
108
|
+
const registerOAuthApplicationBodySchema = z.object({
|
|
109
|
+
redirect_uris: z.array(z.string()).meta({ description: "A list of redirect URIs. Eg: [\"https://client.example.com/callback\"]" }),
|
|
110
|
+
token_endpoint_auth_method: z.enum([
|
|
111
|
+
"none",
|
|
112
|
+
"client_secret_basic",
|
|
113
|
+
"client_secret_post"
|
|
114
|
+
]).meta({ description: "The authentication method for the token endpoint. Eg: \"client_secret_basic\"" }).default("client_secret_basic").optional(),
|
|
115
|
+
grant_types: z.array(z.enum([
|
|
116
|
+
"authorization_code",
|
|
117
|
+
"implicit",
|
|
118
|
+
"password",
|
|
119
|
+
"client_credentials",
|
|
120
|
+
"refresh_token",
|
|
121
|
+
"urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
122
|
+
"urn:ietf:params:oauth:grant-type:saml2-bearer"
|
|
123
|
+
])).meta({ description: "The grant types supported by the application. Eg: [\"authorization_code\"]" }).default(["authorization_code"]).optional(),
|
|
124
|
+
response_types: z.array(z.enum(["code", "token"])).meta({ description: "The response types supported by the application. Eg: [\"code\"]" }).default(["code"]).optional(),
|
|
125
|
+
client_name: z.string().meta({ description: "The name of the application. Eg: \"My App\"" }).optional(),
|
|
126
|
+
client_uri: z.string().meta({ description: "The URI of the application. Eg: \"https://client.example.com\"" }).optional(),
|
|
127
|
+
logo_uri: z.string().meta({ description: "The URI of the application logo. Eg: \"https://client.example.com/logo.png\"" }).optional(),
|
|
128
|
+
scope: z.string().meta({ description: "The scopes supported by the application. Separated by spaces. Eg: \"profile email\"" }).optional(),
|
|
129
|
+
contacts: z.array(z.string()).meta({ description: "The contact information for the application. Eg: [\"admin@example.com\"]" }).optional(),
|
|
130
|
+
tos_uri: z.string().meta({ description: "The URI of the application terms of service. Eg: \"https://client.example.com/tos\"" }).optional(),
|
|
131
|
+
policy_uri: z.string().meta({ description: "The URI of the application privacy policy. Eg: \"https://client.example.com/policy\"" }).optional(),
|
|
132
|
+
jwks_uri: z.string().meta({ description: "The URI of the application JWKS. Eg: \"https://client.example.com/jwks\"" }).optional(),
|
|
133
|
+
jwks: z.record(z.any(), z.any()).meta({ description: "The JWKS of the application. Eg: {\"keys\": [{\"kty\": \"RSA\", \"alg\": \"RS256\", \"use\": \"sig\", \"n\": \"...\", \"e\": \"...\"}]}" }).optional(),
|
|
134
|
+
metadata: z.record(z.any(), z.any()).meta({ description: "The metadata of the application. Eg: {\"key\": \"value\"}" }).optional(),
|
|
135
|
+
software_id: z.string().meta({ description: "The software ID of the application. Eg: \"my-software\"" }).optional(),
|
|
136
|
+
software_version: z.string().meta({ description: "The software version of the application. Eg: \"1.0.0\"" }).optional(),
|
|
137
|
+
software_statement: z.string().meta({ description: "The software statement of the application." }).optional()
|
|
138
|
+
});
|
|
139
|
+
const DEFAULT_CODE_EXPIRES_IN = 600;
|
|
140
|
+
const DEFAULT_ACCESS_TOKEN_EXPIRES_IN = 3600;
|
|
141
|
+
const DEFAULT_REFRESH_TOKEN_EXPIRES_IN = 604800;
|
|
142
|
+
/**
|
|
143
|
+
* OpenID Connect (OIDC) plugin for Better Auth. This plugin implements the
|
|
144
|
+
* authorization code flow and the token exchange flow. It also implements the
|
|
145
|
+
* userinfo endpoint.
|
|
146
|
+
*
|
|
147
|
+
* @param options - The options for the OIDC plugin.
|
|
148
|
+
* @returns A Better Auth plugin.
|
|
149
|
+
*/
|
|
150
|
+
const oidcProvider = (options) => {
|
|
151
|
+
const modelName = {
|
|
152
|
+
oauthClient: "oauthApplication",
|
|
153
|
+
oauthAccessToken: "oauthAccessToken",
|
|
154
|
+
oauthConsent: "oauthConsent"
|
|
155
|
+
};
|
|
156
|
+
const opts = {
|
|
157
|
+
codeExpiresIn: DEFAULT_CODE_EXPIRES_IN,
|
|
158
|
+
defaultScope: "openid",
|
|
159
|
+
accessTokenExpiresIn: DEFAULT_ACCESS_TOKEN_EXPIRES_IN,
|
|
160
|
+
refreshTokenExpiresIn: DEFAULT_REFRESH_TOKEN_EXPIRES_IN,
|
|
161
|
+
allowPlainCodeChallengeMethod: true,
|
|
162
|
+
storeClientSecret: "plain",
|
|
163
|
+
...options,
|
|
164
|
+
scopes: [
|
|
165
|
+
"openid",
|
|
166
|
+
"profile",
|
|
167
|
+
"email",
|
|
168
|
+
"offline_access",
|
|
169
|
+
...options?.scopes || []
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
const trustedClients = options.trustedClients || [];
|
|
173
|
+
/**
|
|
174
|
+
* Store client secret according to the configured storage method
|
|
175
|
+
*/
|
|
176
|
+
async function storeClientSecret(ctx, clientSecret) {
|
|
177
|
+
if (opts.storeClientSecret === "encrypted") return await symmetricEncrypt({
|
|
178
|
+
key: ctx.context.secret,
|
|
179
|
+
data: clientSecret
|
|
180
|
+
});
|
|
181
|
+
if (opts.storeClientSecret === "hashed") return await defaultClientSecretHasher(clientSecret);
|
|
182
|
+
if (typeof opts.storeClientSecret === "object" && "hash" in opts.storeClientSecret) return await opts.storeClientSecret.hash(clientSecret);
|
|
183
|
+
if (typeof opts.storeClientSecret === "object" && "encrypt" in opts.storeClientSecret) return await opts.storeClientSecret.encrypt(clientSecret);
|
|
184
|
+
return clientSecret;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Verify stored client secret against provided client secret
|
|
188
|
+
*/
|
|
189
|
+
async function verifyStoredClientSecret(ctx, storedClientSecret, clientSecret) {
|
|
190
|
+
if (opts.storeClientSecret === "encrypted") return await symmetricDecrypt({
|
|
191
|
+
key: ctx.context.secret,
|
|
192
|
+
data: storedClientSecret
|
|
193
|
+
}) === clientSecret;
|
|
194
|
+
if (opts.storeClientSecret === "hashed") return await defaultClientSecretHasher(clientSecret) === storedClientSecret;
|
|
195
|
+
if (typeof opts.storeClientSecret === "object" && "hash" in opts.storeClientSecret) return await opts.storeClientSecret.hash(clientSecret) === storedClientSecret;
|
|
196
|
+
if (typeof opts.storeClientSecret === "object" && "decrypt" in opts.storeClientSecret) return await opts.storeClientSecret.decrypt(storedClientSecret) === clientSecret;
|
|
197
|
+
return clientSecret === storedClientSecret;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
id: "oidc-provider",
|
|
201
|
+
hooks: { after: [{
|
|
202
|
+
matcher() {
|
|
203
|
+
return true;
|
|
204
|
+
},
|
|
205
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
206
|
+
const loginPromptCookie = await ctx.getSignedCookie("oidc_login_prompt", ctx.context.secret);
|
|
207
|
+
const cookieName = ctx.context.authCookies.sessionToken.name;
|
|
208
|
+
const parsedSetCookieHeader = parseSetCookieHeader(ctx.context.responseHeaders?.get("set-cookie") || "");
|
|
209
|
+
const hasSessionToken = parsedSetCookieHeader.has(cookieName);
|
|
210
|
+
if (!loginPromptCookie || !hasSessionToken) return;
|
|
211
|
+
expireCookie(ctx, {
|
|
212
|
+
name: "oidc_login_prompt",
|
|
213
|
+
attributes: { path: "/" }
|
|
214
|
+
});
|
|
215
|
+
const sessionToken = (parsedSetCookieHeader.get(cookieName)?.value)?.split(".")[0];
|
|
216
|
+
if (!sessionToken) return;
|
|
217
|
+
const session = await ctx.context.internalAdapter.findSession(sessionToken) || ctx.context.newSession;
|
|
218
|
+
if (!session) return;
|
|
219
|
+
ctx.query = JSON.parse(loginPromptCookie);
|
|
220
|
+
const promptSet = parsePrompt(String(ctx.query?.prompt));
|
|
221
|
+
if (promptSet.has("login")) {
|
|
222
|
+
const newPromptSet = new Set(promptSet);
|
|
223
|
+
newPromptSet.delete("login");
|
|
224
|
+
ctx.query = {
|
|
225
|
+
...ctx.query,
|
|
226
|
+
prompt: Array.from(newPromptSet).join(" ")
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
ctx.context.session = session;
|
|
230
|
+
return await authorize(ctx, opts);
|
|
231
|
+
})
|
|
232
|
+
}] },
|
|
233
|
+
endpoints: {
|
|
234
|
+
getOpenIdConfig: createAuthEndpoint("/.well-known/openid-configuration", {
|
|
235
|
+
method: "GET",
|
|
236
|
+
operationId: "getOpenIdConfig",
|
|
237
|
+
metadata: HIDE_METADATA
|
|
238
|
+
}, async (ctx) => {
|
|
239
|
+
const metadata = getMetadata(ctx, options);
|
|
240
|
+
return ctx.json(metadata);
|
|
241
|
+
}),
|
|
242
|
+
oAuth2authorize: createAuthEndpoint("/oauth2/authorize", {
|
|
243
|
+
method: "GET",
|
|
244
|
+
operationId: "oauth2Authorize",
|
|
245
|
+
query: z.record(z.string(), z.any()),
|
|
246
|
+
metadata: { openapi: {
|
|
247
|
+
description: "Authorize an OAuth2 request",
|
|
248
|
+
responses: { "200": {
|
|
249
|
+
description: "Authorization response generated successfully",
|
|
250
|
+
content: { "application/json": { schema: {
|
|
251
|
+
type: "object",
|
|
252
|
+
additionalProperties: true,
|
|
253
|
+
description: "Authorization response, contents depend on the authorize function implementation"
|
|
254
|
+
} } }
|
|
255
|
+
} }
|
|
256
|
+
} }
|
|
257
|
+
}, async (ctx) => {
|
|
258
|
+
return authorize(ctx, opts);
|
|
259
|
+
}),
|
|
260
|
+
oAuthConsent: createAuthEndpoint("/oauth2/consent", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
operationId: "oauth2Consent",
|
|
263
|
+
body: oAuthConsentBodySchema,
|
|
264
|
+
use: [sessionMiddleware],
|
|
265
|
+
metadata: { openapi: {
|
|
266
|
+
description: "Handle OAuth2 consent. Supports both URL parameter-based flows (consent_code in body) and cookie-based flows (signed cookie).",
|
|
267
|
+
requestBody: {
|
|
268
|
+
required: true,
|
|
269
|
+
content: { "application/json": { schema: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
accept: {
|
|
273
|
+
type: "boolean",
|
|
274
|
+
description: "Whether the user accepts or denies the consent request"
|
|
275
|
+
},
|
|
276
|
+
consent_code: {
|
|
277
|
+
type: "string",
|
|
278
|
+
description: "The consent code from the authorization request. Optional if using cookie-based flow."
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
required: ["accept"]
|
|
282
|
+
} } }
|
|
283
|
+
},
|
|
284
|
+
responses: { "200": {
|
|
285
|
+
description: "Consent processed successfully",
|
|
286
|
+
content: { "application/json": { schema: {
|
|
287
|
+
type: "object",
|
|
288
|
+
properties: { redirectURI: {
|
|
289
|
+
type: "string",
|
|
290
|
+
format: "uri",
|
|
291
|
+
description: "The URI to redirect to, either with an authorization code or an error"
|
|
292
|
+
} },
|
|
293
|
+
required: ["redirectURI"]
|
|
294
|
+
} } }
|
|
295
|
+
} }
|
|
296
|
+
} }
|
|
297
|
+
}, async (ctx) => {
|
|
298
|
+
let consentCode = ctx.body.consent_code || null;
|
|
299
|
+
if (!consentCode) {
|
|
300
|
+
const cookieValue = await ctx.getSignedCookie("oidc_consent_prompt", ctx.context.secret);
|
|
301
|
+
if (cookieValue) consentCode = cookieValue;
|
|
302
|
+
}
|
|
303
|
+
if (!consentCode) throw new APIError("UNAUTHORIZED", {
|
|
304
|
+
error_description: "consent_code is required (either in body or cookie)",
|
|
305
|
+
error: "invalid_request"
|
|
306
|
+
});
|
|
307
|
+
const verification = await ctx.context.internalAdapter.findVerificationValue(consentCode);
|
|
308
|
+
if (!verification) throw new APIError("UNAUTHORIZED", {
|
|
309
|
+
error_description: "Invalid code",
|
|
310
|
+
error: "invalid_request"
|
|
311
|
+
});
|
|
312
|
+
if (verification.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
313
|
+
error_description: "Code expired",
|
|
314
|
+
error: "invalid_request"
|
|
315
|
+
});
|
|
316
|
+
expireCookie(ctx, {
|
|
317
|
+
name: "oidc_consent_prompt",
|
|
318
|
+
attributes: { path: "/" }
|
|
319
|
+
});
|
|
320
|
+
const value = JSON.parse(verification.value);
|
|
321
|
+
if (!value.requireConsent) throw new APIError("UNAUTHORIZED", {
|
|
322
|
+
error_description: "Consent not required",
|
|
323
|
+
error: "invalid_request"
|
|
324
|
+
});
|
|
325
|
+
if (!ctx.body.accept) {
|
|
326
|
+
await ctx.context.internalAdapter.deleteVerificationValue(verification.id);
|
|
327
|
+
return ctx.json({ redirectURI: `${value.redirectURI}?error=access_denied&error_description=User denied access` });
|
|
328
|
+
}
|
|
329
|
+
const code = generateRandomString(32, "a-z", "A-Z", "0-9");
|
|
330
|
+
const codeExpiresInMs = (opts?.codeExpiresIn ?? DEFAULT_CODE_EXPIRES_IN) * 1e3;
|
|
331
|
+
const expiresAt = new Date(Date.now() + codeExpiresInMs);
|
|
332
|
+
await ctx.context.internalAdapter.updateVerificationValue(verification.id, {
|
|
333
|
+
value: JSON.stringify({
|
|
334
|
+
...value,
|
|
335
|
+
requireConsent: false
|
|
336
|
+
}),
|
|
337
|
+
identifier: code,
|
|
338
|
+
expiresAt
|
|
339
|
+
});
|
|
340
|
+
await ctx.context.adapter.create({
|
|
341
|
+
model: modelName.oauthConsent,
|
|
342
|
+
data: {
|
|
343
|
+
clientId: value.clientId,
|
|
344
|
+
userId: value.userId,
|
|
345
|
+
scopes: value.scope.join(" "),
|
|
346
|
+
consentGiven: true,
|
|
347
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
348
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
const redirectURI = new URL(value.redirectURI);
|
|
352
|
+
redirectURI.searchParams.set("code", code);
|
|
353
|
+
if (value.state) redirectURI.searchParams.set("state", value.state);
|
|
354
|
+
return ctx.json({ redirectURI: redirectURI.toString() });
|
|
355
|
+
}),
|
|
356
|
+
oAuth2token: createAuthEndpoint("/oauth2/token", {
|
|
357
|
+
method: "POST",
|
|
358
|
+
operationId: "oauth2Token",
|
|
359
|
+
body: oAuth2TokenBodySchema,
|
|
360
|
+
metadata: {
|
|
361
|
+
...HIDE_METADATA,
|
|
362
|
+
allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"]
|
|
363
|
+
}
|
|
364
|
+
}, async (ctx) => {
|
|
365
|
+
let { body } = ctx;
|
|
366
|
+
if (!body) throw new APIError("BAD_REQUEST", {
|
|
367
|
+
error_description: "request body not found",
|
|
368
|
+
error: "invalid_request"
|
|
369
|
+
});
|
|
370
|
+
if (body instanceof FormData) body = Object.fromEntries(body.entries());
|
|
371
|
+
if (!(body instanceof Object)) throw new APIError("BAD_REQUEST", {
|
|
372
|
+
error_description: "request body is not an object",
|
|
373
|
+
error: "invalid_request"
|
|
374
|
+
});
|
|
375
|
+
let { client_id, client_secret } = body;
|
|
376
|
+
const authorization = ctx.request?.headers.get("authorization") || null;
|
|
377
|
+
if (authorization && !client_id && !client_secret && authorization.startsWith("Basic ")) try {
|
|
378
|
+
const encoded = authorization.replace("Basic ", "");
|
|
379
|
+
const decoded = new TextDecoder().decode(base64.decode(encoded));
|
|
380
|
+
if (!decoded.includes(":")) throw new APIError("UNAUTHORIZED", {
|
|
381
|
+
error_description: "invalid authorization header format",
|
|
382
|
+
error: "invalid_client"
|
|
383
|
+
});
|
|
384
|
+
const [id, secret] = decoded.split(":");
|
|
385
|
+
if (!id || !secret) throw new APIError("UNAUTHORIZED", {
|
|
386
|
+
error_description: "invalid authorization header format",
|
|
387
|
+
error: "invalid_client"
|
|
388
|
+
});
|
|
389
|
+
client_id = id;
|
|
390
|
+
client_secret = secret;
|
|
391
|
+
} catch {
|
|
392
|
+
throw new APIError("UNAUTHORIZED", {
|
|
393
|
+
error_description: "invalid authorization header format",
|
|
394
|
+
error: "invalid_client"
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
const now = Date.now();
|
|
398
|
+
const iat = Math.floor(now / 1e3);
|
|
399
|
+
const exp = iat + (opts.accessTokenExpiresIn ?? 3600);
|
|
400
|
+
const accessTokenExpiresAt = /* @__PURE__ */ new Date(exp * 1e3);
|
|
401
|
+
const refreshTokenExpiresAt = /* @__PURE__ */ new Date((iat + (opts.refreshTokenExpiresIn ?? 604800)) * 1e3);
|
|
402
|
+
const { grant_type, code, redirect_uri, refresh_token, code_verifier } = body;
|
|
403
|
+
if (grant_type === "refresh_token") {
|
|
404
|
+
if (!refresh_token) throw new APIError("BAD_REQUEST", {
|
|
405
|
+
error_description: "refresh_token is required",
|
|
406
|
+
error: "invalid_request"
|
|
407
|
+
});
|
|
408
|
+
const token = await ctx.context.adapter.findOne({
|
|
409
|
+
model: modelName.oauthAccessToken,
|
|
410
|
+
where: [{
|
|
411
|
+
field: "refreshToken",
|
|
412
|
+
value: refresh_token.toString()
|
|
413
|
+
}]
|
|
414
|
+
});
|
|
415
|
+
if (!token) throw new APIError("UNAUTHORIZED", {
|
|
416
|
+
error_description: "invalid refresh token",
|
|
417
|
+
error: "invalid_grant"
|
|
418
|
+
});
|
|
419
|
+
if (token.clientId !== client_id?.toString()) throw new APIError("UNAUTHORIZED", {
|
|
420
|
+
error_description: "invalid client_id",
|
|
421
|
+
error: "invalid_client"
|
|
422
|
+
});
|
|
423
|
+
if (token.refreshTokenExpiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
424
|
+
error_description: "refresh token expired",
|
|
425
|
+
error: "invalid_grant"
|
|
426
|
+
});
|
|
427
|
+
const accessToken = generateRandomString(32, "a-z", "A-Z");
|
|
428
|
+
const newRefreshToken = generateRandomString(32, "a-z", "A-Z");
|
|
429
|
+
await ctx.context.adapter.create({
|
|
430
|
+
model: modelName.oauthAccessToken,
|
|
431
|
+
data: {
|
|
432
|
+
accessToken,
|
|
433
|
+
refreshToken: newRefreshToken,
|
|
434
|
+
accessTokenExpiresAt,
|
|
435
|
+
refreshTokenExpiresAt,
|
|
436
|
+
clientId: client_id.toString(),
|
|
437
|
+
userId: token.userId,
|
|
438
|
+
scopes: token.scopes,
|
|
439
|
+
createdAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
440
|
+
updatedAt: /* @__PURE__ */ new Date(iat * 1e3)
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
return ctx.json({
|
|
444
|
+
access_token: accessToken,
|
|
445
|
+
token_type: "Bearer",
|
|
446
|
+
expires_in: opts.accessTokenExpiresIn,
|
|
447
|
+
refresh_token: newRefreshToken,
|
|
448
|
+
scope: token.scopes
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (!code) throw new APIError("BAD_REQUEST", {
|
|
452
|
+
error_description: "code is required",
|
|
453
|
+
error: "invalid_request"
|
|
454
|
+
});
|
|
455
|
+
if (options.requirePKCE && !code_verifier) throw new APIError("BAD_REQUEST", {
|
|
456
|
+
error_description: "code verifier is missing",
|
|
457
|
+
error: "invalid_request"
|
|
458
|
+
});
|
|
459
|
+
/**
|
|
460
|
+
* We need to check if the code is valid before we can proceed
|
|
461
|
+
* with the rest of the request.
|
|
462
|
+
*/
|
|
463
|
+
const verificationValue = await ctx.context.internalAdapter.findVerificationValue(code.toString());
|
|
464
|
+
if (!verificationValue) throw new APIError("UNAUTHORIZED", {
|
|
465
|
+
error_description: "invalid code",
|
|
466
|
+
error: "invalid_grant"
|
|
467
|
+
});
|
|
468
|
+
if (verificationValue.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
469
|
+
error_description: "code expired",
|
|
470
|
+
error: "invalid_grant"
|
|
471
|
+
});
|
|
472
|
+
await ctx.context.internalAdapter.deleteVerificationValue(verificationValue.id);
|
|
473
|
+
if (!client_id) throw new APIError("UNAUTHORIZED", {
|
|
474
|
+
error_description: "client_id is required",
|
|
475
|
+
error: "invalid_client"
|
|
476
|
+
});
|
|
477
|
+
if (!grant_type) throw new APIError("BAD_REQUEST", {
|
|
478
|
+
error_description: "grant_type is required",
|
|
479
|
+
error: "invalid_request"
|
|
480
|
+
});
|
|
481
|
+
if (grant_type !== "authorization_code") throw new APIError("BAD_REQUEST", {
|
|
482
|
+
error_description: "grant_type must be 'authorization_code'",
|
|
483
|
+
error: "unsupported_grant_type"
|
|
484
|
+
});
|
|
485
|
+
if (!redirect_uri) throw new APIError("BAD_REQUEST", {
|
|
486
|
+
error_description: "redirect_uri is required",
|
|
487
|
+
error: "invalid_request"
|
|
488
|
+
});
|
|
489
|
+
const client = await getClient(client_id.toString(), trustedClients);
|
|
490
|
+
if (!client) throw new APIError("UNAUTHORIZED", {
|
|
491
|
+
error_description: "invalid client_id",
|
|
492
|
+
error: "invalid_client"
|
|
493
|
+
});
|
|
494
|
+
if (client.disabled) throw new APIError("UNAUTHORIZED", {
|
|
495
|
+
error_description: "client is disabled",
|
|
496
|
+
error: "invalid_client"
|
|
497
|
+
});
|
|
498
|
+
const value = JSON.parse(verificationValue.value);
|
|
499
|
+
if (value.clientId !== client_id.toString()) throw new APIError("UNAUTHORIZED", {
|
|
500
|
+
error_description: "invalid client_id",
|
|
501
|
+
error: "invalid_client"
|
|
502
|
+
});
|
|
503
|
+
if (value.redirectURI !== redirect_uri.toString()) throw new APIError("UNAUTHORIZED", {
|
|
504
|
+
error_description: "invalid redirect_uri",
|
|
505
|
+
error: "invalid_client"
|
|
506
|
+
});
|
|
507
|
+
if (value.codeChallenge && !code_verifier) throw new APIError("BAD_REQUEST", {
|
|
508
|
+
error_description: "code verifier is missing",
|
|
509
|
+
error: "invalid_request"
|
|
510
|
+
});
|
|
511
|
+
if (client.type === "public") {
|
|
512
|
+
if (!code_verifier) throw new APIError("BAD_REQUEST", {
|
|
513
|
+
error_description: "code verifier is required for public clients",
|
|
514
|
+
error: "invalid_request"
|
|
515
|
+
});
|
|
516
|
+
} else {
|
|
517
|
+
if (!client.clientSecret || !client_secret) throw new APIError("UNAUTHORIZED", {
|
|
518
|
+
error_description: "client_secret is required for confidential clients",
|
|
519
|
+
error: "invalid_client"
|
|
520
|
+
});
|
|
521
|
+
if (!await verifyStoredClientSecret(ctx, client.clientSecret, client_secret.toString())) throw new APIError("UNAUTHORIZED", {
|
|
522
|
+
error_description: "invalid client_secret",
|
|
523
|
+
error: "invalid_client"
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
if ((value.codeChallengeMethod === "plain" ? code_verifier : await createHash("SHA-256", "base64urlnopad").digest(code_verifier)) !== value.codeChallenge) throw new APIError("UNAUTHORIZED", {
|
|
527
|
+
error_description: "code verification failed",
|
|
528
|
+
error: "invalid_request"
|
|
529
|
+
});
|
|
530
|
+
const requestedScopes = value.scope;
|
|
531
|
+
await ctx.context.internalAdapter.deleteVerificationValue(verificationValue.id);
|
|
532
|
+
const accessToken = generateRandomString(32, "a-z", "A-Z");
|
|
533
|
+
const refreshToken = generateRandomString(32, "A-Z", "a-z");
|
|
534
|
+
await ctx.context.adapter.create({
|
|
535
|
+
model: modelName.oauthAccessToken,
|
|
536
|
+
data: {
|
|
537
|
+
accessToken,
|
|
538
|
+
refreshToken,
|
|
539
|
+
accessTokenExpiresAt,
|
|
540
|
+
refreshTokenExpiresAt,
|
|
541
|
+
clientId: client_id.toString(),
|
|
542
|
+
userId: value.userId,
|
|
543
|
+
scopes: requestedScopes.join(" "),
|
|
544
|
+
createdAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
545
|
+
updatedAt: /* @__PURE__ */ new Date(iat * 1e3)
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
const user = await ctx.context.internalAdapter.findUserById(value.userId);
|
|
549
|
+
if (!user) throw new APIError("UNAUTHORIZED", {
|
|
550
|
+
error_description: "user not found",
|
|
551
|
+
error: "invalid_grant"
|
|
552
|
+
});
|
|
553
|
+
const profile = {
|
|
554
|
+
given_name: user.name.split(" ")[0],
|
|
555
|
+
family_name: user.name.split(" ")[1],
|
|
556
|
+
name: user.name,
|
|
557
|
+
profile: user.image,
|
|
558
|
+
updated_at: new Date(user.updatedAt).toISOString()
|
|
559
|
+
};
|
|
560
|
+
const email = {
|
|
561
|
+
email: user.email,
|
|
562
|
+
email_verified: user.emailVerified
|
|
563
|
+
};
|
|
564
|
+
const userClaims = {
|
|
565
|
+
...requestedScopes.includes("profile") ? profile : {},
|
|
566
|
+
...requestedScopes.includes("email") ? email : {}
|
|
567
|
+
};
|
|
568
|
+
const additionalUserClaims = options.getAdditionalUserInfoClaim ? await options.getAdditionalUserInfoClaim(user, requestedScopes, client) : {};
|
|
569
|
+
const payload = {
|
|
570
|
+
sub: user.id,
|
|
571
|
+
aud: client_id.toString(),
|
|
572
|
+
iat,
|
|
573
|
+
auth_time: ctx.context.session ? new Date(ctx.context.session.session.createdAt).getTime() : void 0,
|
|
574
|
+
nonce: value.nonce,
|
|
575
|
+
acr: "urn:mace:incommon:iap:silver",
|
|
576
|
+
...userClaims,
|
|
577
|
+
...additionalUserClaims
|
|
578
|
+
};
|
|
579
|
+
const expirationTime = Math.floor(Date.now() / 1e3) + (opts?.accessTokenExpiresIn ?? DEFAULT_ACCESS_TOKEN_EXPIRES_IN);
|
|
580
|
+
let idToken;
|
|
581
|
+
if (options.useJWTPlugin) {
|
|
582
|
+
const jwtPlugin = ctx.context.getPlugin("jwt");
|
|
583
|
+
if (!jwtPlugin) {
|
|
584
|
+
ctx.context.logger.error("OIDC: `useJWTPlugin` is enabled but the JWT plugin is not available. Make sure you have the JWT Plugin in your plugins array or set `useJWTPlugin` to false.");
|
|
585
|
+
throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
586
|
+
error_description: "JWT plugin is not enabled",
|
|
587
|
+
error: "internal_server_error"
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
idToken = await getJwtToken({
|
|
591
|
+
...ctx,
|
|
592
|
+
context: {
|
|
593
|
+
...ctx.context,
|
|
594
|
+
session: {
|
|
595
|
+
session: {
|
|
596
|
+
id: generateRandomString(32, "a-z", "A-Z"),
|
|
597
|
+
createdAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
598
|
+
updatedAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
599
|
+
userId: user.id,
|
|
600
|
+
expiresAt: accessTokenExpiresAt,
|
|
601
|
+
token: accessToken,
|
|
602
|
+
ipAddress: ctx.request?.headers.get("x-forwarded-for")
|
|
603
|
+
},
|
|
604
|
+
user
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}, {
|
|
608
|
+
...jwtPlugin.options,
|
|
609
|
+
jwt: {
|
|
610
|
+
...jwtPlugin.options?.jwt,
|
|
611
|
+
getSubject: () => user.id,
|
|
612
|
+
audience: client_id.toString(),
|
|
613
|
+
issuer: jwtPlugin.options?.jwt?.issuer ?? ctx.context.options.baseURL,
|
|
614
|
+
expirationTime,
|
|
615
|
+
definePayload: () => payload
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
} else idToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt(iat).setExpirationTime(accessTokenExpiresAt).sign(new TextEncoder().encode(client.clientSecret));
|
|
619
|
+
return ctx.json({
|
|
620
|
+
access_token: accessToken,
|
|
621
|
+
token_type: "Bearer",
|
|
622
|
+
expires_in: opts.accessTokenExpiresIn,
|
|
623
|
+
refresh_token: requestedScopes.includes("offline_access") ? refreshToken : void 0,
|
|
624
|
+
scope: requestedScopes.join(" "),
|
|
625
|
+
id_token: requestedScopes.includes("openid") ? idToken : void 0
|
|
626
|
+
}, { headers: {
|
|
627
|
+
"Cache-Control": "no-store",
|
|
628
|
+
Pragma: "no-cache"
|
|
629
|
+
} });
|
|
630
|
+
}),
|
|
631
|
+
oAuth2userInfo: createAuthEndpoint("/oauth2/userinfo", {
|
|
632
|
+
method: "GET",
|
|
633
|
+
operationId: "oauth2Userinfo",
|
|
634
|
+
metadata: {
|
|
635
|
+
...HIDE_METADATA,
|
|
636
|
+
openapi: {
|
|
637
|
+
description: "Get OAuth2 user information",
|
|
638
|
+
responses: { "200": {
|
|
639
|
+
description: "User information retrieved successfully",
|
|
640
|
+
content: { "application/json": { schema: {
|
|
641
|
+
type: "object",
|
|
642
|
+
properties: {
|
|
643
|
+
sub: {
|
|
644
|
+
type: "string",
|
|
645
|
+
description: "Subject identifier (user ID)"
|
|
646
|
+
},
|
|
647
|
+
email: {
|
|
648
|
+
type: "string",
|
|
649
|
+
format: "email",
|
|
650
|
+
nullable: true,
|
|
651
|
+
description: "User's email address, included if 'email' scope is granted"
|
|
652
|
+
},
|
|
653
|
+
name: {
|
|
654
|
+
type: "string",
|
|
655
|
+
nullable: true,
|
|
656
|
+
description: "User's full name, included if 'profile' scope is granted"
|
|
657
|
+
},
|
|
658
|
+
picture: {
|
|
659
|
+
type: "string",
|
|
660
|
+
format: "uri",
|
|
661
|
+
nullable: true,
|
|
662
|
+
description: "User's profile picture URL, included if 'profile' scope is granted"
|
|
663
|
+
},
|
|
664
|
+
given_name: {
|
|
665
|
+
type: "string",
|
|
666
|
+
nullable: true,
|
|
667
|
+
description: "User's given name, included if 'profile' scope is granted"
|
|
668
|
+
},
|
|
669
|
+
family_name: {
|
|
670
|
+
type: "string",
|
|
671
|
+
nullable: true,
|
|
672
|
+
description: "User's family name, included if 'profile' scope is granted"
|
|
673
|
+
},
|
|
674
|
+
email_verified: {
|
|
675
|
+
type: "boolean",
|
|
676
|
+
nullable: true,
|
|
677
|
+
description: "Whether the email is verified, included if 'email' scope is granted"
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
required: ["sub"]
|
|
681
|
+
} } }
|
|
682
|
+
} }
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}, async (ctx) => {
|
|
686
|
+
if (!ctx.request) throw new APIError("UNAUTHORIZED", {
|
|
687
|
+
error_description: "request not found",
|
|
688
|
+
error: "invalid_request"
|
|
689
|
+
});
|
|
690
|
+
const authorization = ctx.request.headers.get("authorization");
|
|
691
|
+
if (!authorization) throw new APIError("UNAUTHORIZED", {
|
|
692
|
+
error_description: "authorization header not found",
|
|
693
|
+
error: "invalid_request"
|
|
694
|
+
});
|
|
695
|
+
const token = authorization.replace("Bearer ", "");
|
|
696
|
+
const accessToken = await ctx.context.adapter.findOne({
|
|
697
|
+
model: modelName.oauthAccessToken,
|
|
698
|
+
where: [{
|
|
699
|
+
field: "accessToken",
|
|
700
|
+
value: token
|
|
701
|
+
}]
|
|
702
|
+
});
|
|
703
|
+
if (!accessToken) throw new APIError("UNAUTHORIZED", {
|
|
704
|
+
error_description: "invalid access token",
|
|
705
|
+
error: "invalid_token"
|
|
706
|
+
});
|
|
707
|
+
if (accessToken.accessTokenExpiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
708
|
+
error_description: "The Access Token expired",
|
|
709
|
+
error: "invalid_token"
|
|
710
|
+
});
|
|
711
|
+
const client = await getClient(accessToken.clientId, trustedClients);
|
|
712
|
+
if (!client) throw new APIError("UNAUTHORIZED", {
|
|
713
|
+
error_description: "client not found",
|
|
714
|
+
error: "invalid_token"
|
|
715
|
+
});
|
|
716
|
+
const user = await ctx.context.internalAdapter.findUserById(accessToken.userId);
|
|
717
|
+
if (!user) throw new APIError("UNAUTHORIZED", {
|
|
718
|
+
error_description: "user not found",
|
|
719
|
+
error: "invalid_token"
|
|
720
|
+
});
|
|
721
|
+
const requestedScopes = accessToken.scopes.split(" ");
|
|
722
|
+
const baseUserClaims = {
|
|
723
|
+
sub: user.id,
|
|
724
|
+
email: requestedScopes.includes("email") ? user.email : void 0,
|
|
725
|
+
name: requestedScopes.includes("profile") ? user.name : void 0,
|
|
726
|
+
picture: requestedScopes.includes("profile") ? user.image : void 0,
|
|
727
|
+
given_name: requestedScopes.includes("profile") ? user.name.split(" ")[0] : void 0,
|
|
728
|
+
family_name: requestedScopes.includes("profile") ? user.name.split(" ")[1] : void 0,
|
|
729
|
+
email_verified: requestedScopes.includes("email") ? user.emailVerified : void 0
|
|
730
|
+
};
|
|
731
|
+
const userClaims = options.getAdditionalUserInfoClaim ? await options.getAdditionalUserInfoClaim(user, requestedScopes, client) : baseUserClaims;
|
|
732
|
+
return ctx.json({
|
|
733
|
+
...baseUserClaims,
|
|
734
|
+
...userClaims
|
|
735
|
+
});
|
|
736
|
+
}),
|
|
737
|
+
registerOAuthApplication: createAuthEndpoint("/oauth2/register", {
|
|
738
|
+
method: "POST",
|
|
739
|
+
body: registerOAuthApplicationBodySchema,
|
|
740
|
+
metadata: { openapi: {
|
|
741
|
+
description: "Register an OAuth2 application",
|
|
742
|
+
responses: { "200": {
|
|
743
|
+
description: "OAuth2 application registered successfully",
|
|
744
|
+
content: { "application/json": { schema: {
|
|
745
|
+
type: "object",
|
|
746
|
+
properties: {
|
|
747
|
+
name: {
|
|
748
|
+
type: "string",
|
|
749
|
+
description: "Name of the OAuth2 application"
|
|
750
|
+
},
|
|
751
|
+
icon: {
|
|
752
|
+
type: "string",
|
|
753
|
+
nullable: true,
|
|
754
|
+
description: "Icon URL for the application"
|
|
755
|
+
},
|
|
756
|
+
metadata: {
|
|
757
|
+
type: "object",
|
|
758
|
+
additionalProperties: true,
|
|
759
|
+
nullable: true,
|
|
760
|
+
description: "Additional metadata for the application"
|
|
761
|
+
},
|
|
762
|
+
clientId: {
|
|
763
|
+
type: "string",
|
|
764
|
+
description: "Unique identifier for the client"
|
|
765
|
+
},
|
|
766
|
+
clientSecret: {
|
|
767
|
+
type: "string",
|
|
768
|
+
description: "Secret key for the client"
|
|
769
|
+
},
|
|
770
|
+
redirectURLs: {
|
|
771
|
+
type: "array",
|
|
772
|
+
items: {
|
|
773
|
+
type: "string",
|
|
774
|
+
format: "uri"
|
|
775
|
+
},
|
|
776
|
+
description: "List of allowed redirect URLs"
|
|
777
|
+
},
|
|
778
|
+
type: {
|
|
779
|
+
type: "string",
|
|
780
|
+
description: "Type of the client",
|
|
781
|
+
enum: ["web"]
|
|
782
|
+
},
|
|
783
|
+
authenticationScheme: {
|
|
784
|
+
type: "string",
|
|
785
|
+
description: "Authentication scheme used by the client",
|
|
786
|
+
enum: ["client_secret"]
|
|
787
|
+
},
|
|
788
|
+
disabled: {
|
|
789
|
+
type: "boolean",
|
|
790
|
+
description: "Whether the client is disabled",
|
|
791
|
+
enum: [false]
|
|
792
|
+
},
|
|
793
|
+
userId: {
|
|
794
|
+
type: "string",
|
|
795
|
+
nullable: true,
|
|
796
|
+
description: "ID of the user who registered the client, null if registered anonymously"
|
|
797
|
+
},
|
|
798
|
+
createdAt: {
|
|
799
|
+
type: "string",
|
|
800
|
+
format: "date-time",
|
|
801
|
+
description: "Creation timestamp"
|
|
802
|
+
},
|
|
803
|
+
updatedAt: {
|
|
804
|
+
type: "string",
|
|
805
|
+
format: "date-time",
|
|
806
|
+
description: "Last update timestamp"
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
required: [
|
|
810
|
+
"name",
|
|
811
|
+
"clientId",
|
|
812
|
+
"clientSecret",
|
|
813
|
+
"redirectURLs",
|
|
814
|
+
"type",
|
|
815
|
+
"authenticationScheme",
|
|
816
|
+
"disabled",
|
|
817
|
+
"createdAt",
|
|
818
|
+
"updatedAt"
|
|
819
|
+
]
|
|
820
|
+
} } }
|
|
821
|
+
} }
|
|
822
|
+
} }
|
|
823
|
+
}, async (ctx) => {
|
|
824
|
+
const body = ctx.body;
|
|
825
|
+
const session = await getSessionFromCtx(ctx);
|
|
826
|
+
if (!session && !options.allowDynamicClientRegistration) throw new APIError("UNAUTHORIZED", {
|
|
827
|
+
error: "invalid_token",
|
|
828
|
+
error_description: "Authentication required for client registration"
|
|
829
|
+
});
|
|
830
|
+
if ((!body.grant_types || body.grant_types.includes("authorization_code") || body.grant_types.includes("implicit")) && (!body.redirect_uris || body.redirect_uris.length === 0)) throw new APIError("BAD_REQUEST", {
|
|
831
|
+
error: "invalid_redirect_uri",
|
|
832
|
+
error_description: "Redirect URIs are required for authorization_code and implicit grant types"
|
|
833
|
+
});
|
|
834
|
+
if (body.grant_types && body.response_types) {
|
|
835
|
+
if (body.grant_types.includes("authorization_code") && !body.response_types.includes("code")) throw new APIError("BAD_REQUEST", {
|
|
836
|
+
error: "invalid_client_metadata",
|
|
837
|
+
error_description: "When 'authorization_code' grant type is used, 'code' response type must be included"
|
|
838
|
+
});
|
|
839
|
+
if (body.grant_types.includes("implicit") && !body.response_types.includes("token")) throw new APIError("BAD_REQUEST", {
|
|
840
|
+
error: "invalid_client_metadata",
|
|
841
|
+
error_description: "When 'implicit' grant type is used, 'token' response type must be included"
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
const clientId = options.generateClientId?.() || generateRandomString(32, "a-z", "A-Z");
|
|
845
|
+
const clientSecret = options.generateClientSecret?.() || generateRandomString(32, "a-z", "A-Z");
|
|
846
|
+
const storedClientSecret = await storeClientSecret(ctx, clientSecret);
|
|
847
|
+
const client = await ctx.context.adapter.create({
|
|
848
|
+
model: modelName.oauthClient,
|
|
849
|
+
data: {
|
|
850
|
+
name: body.client_name,
|
|
851
|
+
icon: body.logo_uri,
|
|
852
|
+
metadata: body.metadata ? JSON.stringify(body.metadata) : null,
|
|
853
|
+
clientId,
|
|
854
|
+
clientSecret: storedClientSecret,
|
|
855
|
+
redirectUrls: body.redirect_uris.join(","),
|
|
856
|
+
type: "web",
|
|
857
|
+
authenticationScheme: body.token_endpoint_auth_method || "client_secret_basic",
|
|
858
|
+
disabled: false,
|
|
859
|
+
userId: session?.session.userId,
|
|
860
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
861
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
return ctx.json({
|
|
865
|
+
client_id: clientId,
|
|
866
|
+
...client.type !== "public" ? {
|
|
867
|
+
client_secret: clientSecret,
|
|
868
|
+
client_secret_expires_at: 0
|
|
869
|
+
} : {},
|
|
870
|
+
client_id_issued_at: Math.floor(Date.now() / 1e3),
|
|
871
|
+
client_secret_expires_at: 0,
|
|
872
|
+
redirect_uris: body.redirect_uris,
|
|
873
|
+
token_endpoint_auth_method: body.token_endpoint_auth_method || "client_secret_basic",
|
|
874
|
+
grant_types: body.grant_types || ["authorization_code"],
|
|
875
|
+
response_types: body.response_types || ["code"],
|
|
876
|
+
client_name: body.client_name,
|
|
877
|
+
client_uri: body.client_uri,
|
|
878
|
+
logo_uri: body.logo_uri,
|
|
879
|
+
scope: body.scope,
|
|
880
|
+
contacts: body.contacts,
|
|
881
|
+
tos_uri: body.tos_uri,
|
|
882
|
+
policy_uri: body.policy_uri,
|
|
883
|
+
jwks_uri: body.jwks_uri,
|
|
884
|
+
jwks: body.jwks,
|
|
885
|
+
software_id: body.software_id,
|
|
886
|
+
software_version: body.software_version,
|
|
887
|
+
software_statement: body.software_statement,
|
|
888
|
+
metadata: body.metadata
|
|
889
|
+
}, {
|
|
890
|
+
status: 201,
|
|
891
|
+
headers: {
|
|
892
|
+
"Cache-Control": "no-store",
|
|
893
|
+
Pragma: "no-cache"
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
}),
|
|
897
|
+
getOAuthClient: createAuthEndpoint("/oauth2/client/:id", {
|
|
898
|
+
method: "GET",
|
|
899
|
+
use: [sessionMiddleware],
|
|
900
|
+
metadata: { openapi: {
|
|
901
|
+
description: "Get OAuth2 client details",
|
|
902
|
+
responses: { "200": {
|
|
903
|
+
description: "OAuth2 client retrieved successfully",
|
|
904
|
+
content: { "application/json": { schema: {
|
|
905
|
+
type: "object",
|
|
906
|
+
properties: {
|
|
907
|
+
clientId: {
|
|
908
|
+
type: "string",
|
|
909
|
+
description: "Unique identifier for the client"
|
|
910
|
+
},
|
|
911
|
+
name: {
|
|
912
|
+
type: "string",
|
|
913
|
+
description: "Name of the OAuth2 application"
|
|
914
|
+
},
|
|
915
|
+
icon: {
|
|
916
|
+
type: "string",
|
|
917
|
+
nullable: true,
|
|
918
|
+
description: "Icon URL for the application"
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
required: ["clientId", "name"]
|
|
922
|
+
} } }
|
|
923
|
+
} }
|
|
924
|
+
} }
|
|
925
|
+
}, async (ctx) => {
|
|
926
|
+
const client = await getClient(ctx.params.id, trustedClients);
|
|
927
|
+
if (!client) throw new APIError("NOT_FOUND", {
|
|
928
|
+
error_description: "client not found",
|
|
929
|
+
error: "not_found"
|
|
930
|
+
});
|
|
931
|
+
return ctx.json({
|
|
932
|
+
clientId: client.clientId,
|
|
933
|
+
name: client.name,
|
|
934
|
+
icon: client.icon || null
|
|
935
|
+
});
|
|
936
|
+
}),
|
|
937
|
+
endSession: createAuthEndpoint("/oauth2/endsession", {
|
|
938
|
+
method: ["GET", "POST"],
|
|
939
|
+
query: z.object({
|
|
940
|
+
id_token_hint: z.string().optional(),
|
|
941
|
+
logout_hint: z.string().optional(),
|
|
942
|
+
client_id: z.string().optional(),
|
|
943
|
+
post_logout_redirect_uri: z.string().optional(),
|
|
944
|
+
state: z.string().optional(),
|
|
945
|
+
ui_locales: z.string().optional()
|
|
946
|
+
}).optional(),
|
|
947
|
+
metadata: {
|
|
948
|
+
...HIDE_METADATA,
|
|
949
|
+
openapi: {
|
|
950
|
+
description: "RP-Initiated Logout endpoint. Logs out the end-user and optionally redirects to a post-logout URI.",
|
|
951
|
+
parameters: [
|
|
952
|
+
{
|
|
953
|
+
name: "id_token_hint",
|
|
954
|
+
in: "query",
|
|
955
|
+
description: "Previously issued ID Token passed as a hint about the End-User's current authenticated session",
|
|
956
|
+
required: false,
|
|
957
|
+
schema: { type: "string" }
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
name: "logout_hint",
|
|
961
|
+
in: "query",
|
|
962
|
+
description: "Hint to the Authorization Server about the End-User that is logging out",
|
|
963
|
+
required: false,
|
|
964
|
+
schema: { type: "string" }
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
name: "client_id",
|
|
968
|
+
in: "query",
|
|
969
|
+
description: "OAuth 2.0 Client Identifier. Required if post_logout_redirect_uri is used without id_token_hint",
|
|
970
|
+
required: false,
|
|
971
|
+
schema: { type: "string" }
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
name: "post_logout_redirect_uri",
|
|
975
|
+
in: "query",
|
|
976
|
+
description: "URL to which the RP is requesting that the End-User's User Agent be redirected after a logout has been performed",
|
|
977
|
+
required: false,
|
|
978
|
+
schema: {
|
|
979
|
+
type: "string",
|
|
980
|
+
format: "uri"
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
name: "state",
|
|
985
|
+
in: "query",
|
|
986
|
+
description: "Opaque value used by the RP to maintain state between the logout request and the callback",
|
|
987
|
+
required: false,
|
|
988
|
+
schema: { type: "string" }
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
name: "ui_locales",
|
|
992
|
+
in: "query",
|
|
993
|
+
description: "End-User's preferred languages and scripts for the user interface",
|
|
994
|
+
required: false,
|
|
995
|
+
schema: { type: "string" }
|
|
996
|
+
}
|
|
997
|
+
],
|
|
998
|
+
responses: {
|
|
999
|
+
"302": { description: "Redirect to post_logout_redirect_uri or logout confirmation page" },
|
|
1000
|
+
"200": { description: "Logout completed successfully" }
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}, async (ctx) => {
|
|
1005
|
+
const { id_token_hint, client_id, post_logout_redirect_uri, state } = ctx.query || {};
|
|
1006
|
+
let validatedClientId = null;
|
|
1007
|
+
let validatedUserId = null;
|
|
1008
|
+
if (id_token_hint) try {
|
|
1009
|
+
const jwtPlugin = ctx.context.getPlugin("jwt");
|
|
1010
|
+
if (jwtPlugin && jwtPlugin.options && options?.useJWTPlugin) {
|
|
1011
|
+
const verified = await verifyJWT(id_token_hint, jwtPlugin.options);
|
|
1012
|
+
if (verified) {
|
|
1013
|
+
validatedUserId = verified.sub;
|
|
1014
|
+
validatedClientId = verified.aud ? typeof verified.aud === "string" ? verified.aud : verified.aud[0] : null;
|
|
1015
|
+
}
|
|
1016
|
+
} else if (client_id) {
|
|
1017
|
+
const client = await getClient(client_id, trustedClients);
|
|
1018
|
+
if (client && client.clientSecret) try {
|
|
1019
|
+
const { payload } = await jwtVerify(id_token_hint, new TextEncoder().encode(client.clientSecret));
|
|
1020
|
+
validatedUserId = payload.sub;
|
|
1021
|
+
validatedClientId = payload.aud;
|
|
1022
|
+
} catch {}
|
|
1023
|
+
}
|
|
1024
|
+
} catch {
|
|
1025
|
+
ctx.context.logger.debug("Invalid id_token_hint provided to end_session endpoint");
|
|
1026
|
+
}
|
|
1027
|
+
if (client_id) {
|
|
1028
|
+
if (!await getClient(client_id, trustedClients)) throw new APIError("BAD_REQUEST", {
|
|
1029
|
+
error: "invalid_client",
|
|
1030
|
+
error_description: "Invalid client_id"
|
|
1031
|
+
});
|
|
1032
|
+
if (validatedClientId && validatedClientId !== client_id) throw new APIError("BAD_REQUEST", {
|
|
1033
|
+
error: "invalid_request",
|
|
1034
|
+
error_description: "client_id does not match the ID Token's audience"
|
|
1035
|
+
});
|
|
1036
|
+
validatedClientId = client_id;
|
|
1037
|
+
}
|
|
1038
|
+
if (post_logout_redirect_uri) {
|
|
1039
|
+
if (!validatedClientId) throw new APIError("BAD_REQUEST", {
|
|
1040
|
+
error: "invalid_request",
|
|
1041
|
+
error_description: "client_id is required when using post_logout_redirect_uri without a valid id_token_hint"
|
|
1042
|
+
});
|
|
1043
|
+
const client = await getClient(validatedClientId, trustedClients);
|
|
1044
|
+
if (!client) throw new APIError("BAD_REQUEST", {
|
|
1045
|
+
error: "invalid_client",
|
|
1046
|
+
error_description: "Invalid client"
|
|
1047
|
+
});
|
|
1048
|
+
if (!client.redirectUrls.some((registeredUri) => post_logout_redirect_uri === registeredUri)) throw new APIError("BAD_REQUEST", {
|
|
1049
|
+
error: "invalid_request",
|
|
1050
|
+
error_description: "post_logout_redirect_uri is not registered for this client"
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
const session = await getSessionFromCtx(ctx);
|
|
1054
|
+
if (validatedUserId || session) {
|
|
1055
|
+
const userId = validatedUserId || session?.user.id;
|
|
1056
|
+
if (userId) await ctx.context.adapter.deleteMany({
|
|
1057
|
+
model: modelName.oauthAccessToken,
|
|
1058
|
+
where: [{
|
|
1059
|
+
field: "userId",
|
|
1060
|
+
value: userId
|
|
1061
|
+
}]
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
if (session) {
|
|
1065
|
+
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
1066
|
+
expireCookie(ctx, ctx.context.authCookies.sessionToken);
|
|
1067
|
+
}
|
|
1068
|
+
if (post_logout_redirect_uri) try {
|
|
1069
|
+
const redirectUrl = new URL(post_logout_redirect_uri);
|
|
1070
|
+
if (state) redirectUrl.searchParams.set("state", state);
|
|
1071
|
+
return ctx.redirect(redirectUrl.toString());
|
|
1072
|
+
} catch {
|
|
1073
|
+
throw new APIError("BAD_REQUEST", {
|
|
1074
|
+
error: "invalid_request",
|
|
1075
|
+
error_description: "Invalid post_logout_redirect_uri format"
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
return ctx.json({
|
|
1079
|
+
success: true,
|
|
1080
|
+
message: "Logout successful"
|
|
1081
|
+
});
|
|
1082
|
+
})
|
|
1083
|
+
},
|
|
1084
|
+
schema: mergeSchema(schema, options?.schema),
|
|
1085
|
+
get options() {
|
|
1086
|
+
return opts;
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
//#endregion
|
|
1092
|
+
export { getClient, getMetadata, oidcProvider };
|
|
1093
|
+
//# sourceMappingURL=index.mjs.map
|