@gatwech/auth 1.0.0-beta.18
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 +31 -0
- package/dist/access-BO2g7QVe.mjs +45 -0
- package/dist/access-C6O_osEz.mjs +79 -0
- package/dist/access-DT1f6l5k.mjs +51 -0
- package/dist/adapter-factory-BZ2Qiqpn.mjs +640 -0
- package/dist/adapters/drizzle-adapter/index.d.mts +47 -0
- package/dist/adapters/drizzle-adapter/index.mjs +208 -0
- package/dist/adapters/index.d.mts +132 -0
- package/dist/adapters/index.mjs +18 -0
- package/dist/adapters/kysely-adapter/index.d.mts +47 -0
- package/dist/adapters/kysely-adapter/index.mjs +11 -0
- package/dist/adapters/memory-adapter/index.d.mts +14 -0
- package/dist/adapters/memory-adapter/index.mjs +154 -0
- package/dist/adapters/mongodb-adapter/index.d.mts +35 -0
- package/dist/adapters/mongodb-adapter/index.mjs +315 -0
- package/dist/adapters/prisma-adapter/index.d.mts +34 -0
- package/dist/adapters/prisma-adapter/index.mjs +159 -0
- package/dist/adapters/test.d.mts +81 -0
- package/dist/adapters/test.mjs +793 -0
- package/dist/admin-Bcq53Nwj.mjs +743 -0
- package/dist/admin-DtfO1PLg.d.mts +1689 -0
- package/dist/anonymous-CRt1imBW.mjs +107 -0
- package/dist/api/index.d.mts +6 -0
- package/dist/api/index.mjs +17 -0
- package/dist/api-0pR_I-Nx.mjs +3139 -0
- package/dist/auth-CBUoBX6o.mjs +233 -0
- package/dist/bearer-Djf_awfo.mjs +63 -0
- package/dist/bun-sqlite-dialect-CeNvMaZz.mjs +155 -0
- package/dist/captcha-BqaMxlKC.mjs +178 -0
- package/dist/chunk-BK_LfMhE.mjs +40 -0
- package/dist/client/index.d.mts +186 -0
- package/dist/client/index.mjs +7 -0
- package/dist/client/plugins/index.d.mts +949 -0
- package/dist/client/plugins/index.mjs +539 -0
- package/dist/client/react/index.d.mts +179 -0
- package/dist/client/react/index.mjs +69 -0
- package/dist/client-BOX93C3d.d.mts +1250 -0
- package/dist/client-CNY0tn9g.d.mts +628 -0
- package/dist/client-CygJhqeB.mjs +29 -0
- package/dist/client-WiN43tAS.mjs +17 -0
- package/dist/client-aExCkfwm.mjs +30 -0
- package/dist/cookies/index.d.mts +7 -0
- package/dist/cookies/index.mjs +7 -0
- package/dist/cookies-cJObLwn1.mjs +597 -0
- package/dist/crypto/index.d.mts +44 -0
- package/dist/crypto/index.mjs +4 -0
- package/dist/crypto-Bb42eMrH.mjs +70 -0
- package/dist/custom-session-L3t7ecsd.mjs +67 -0
- package/dist/date-Cx2yCiC4.mjs +115 -0
- package/dist/db/index.d.mts +7 -0
- package/dist/db/index.mjs +15 -0
- package/dist/db-BrstmDhc.mjs +1124 -0
- package/dist/device-authorization-V4s3h4Mj.mjs +557 -0
- package/dist/dialect-CeddeGHf.mjs +72 -0
- package/dist/email-otp-BtDHSw14.mjs +592 -0
- package/dist/esm-C-QicDNz.mjs +4090 -0
- package/dist/generic-oauth-CL5X27ZI.mjs +445 -0
- package/dist/get-request-ip-DSjrEpGo.mjs +27 -0
- package/dist/get-tables-BQP7YW9c.mjs +249 -0
- package/dist/has-permission-BpAr7uCn.mjs +14 -0
- package/dist/haveibeenpwned-cMpJvmzI.mjs +43 -0
- package/dist/helper-tmxW3BHj.d.mts +21 -0
- package/dist/index-34ONrP8l.d.mts +503 -0
- package/dist/index-65G5U5DB-DSdjzreF.d.mts +2543 -0
- package/dist/index-B-XKqFvY.d.mts +30 -0
- package/dist/index-B3n8e37d.d.mts +165 -0
- package/dist/index-BBm8nQ9Q.d.mts +42 -0
- package/dist/index-BHoQlf7a.d.mts +7699 -0
- package/dist/index-BYMI9r_d.d.mts +257 -0
- package/dist/index-BqhKCbPH.d.mts +36 -0
- package/dist/index-Buhn3hkR.d.mts +433 -0
- package/dist/index-C-u1h_WX.d.mts +174 -0
- package/dist/index-C7fQuk-V.d.mts +41 -0
- package/dist/index-CM-CFWwr.d.mts +1919 -0
- package/dist/index-CuFzhoS6.d.mts +359 -0
- package/dist/index-Cxi7TbLE.d.mts +11347 -0
- package/dist/index-DB7t8whN.d.mts +1 -0
- package/dist/index-DKNNV1kb.d.mts +626 -0
- package/dist/index-DR0xXno9.d.mts +113 -0
- package/dist/index-D_k662QG.d.mts +833 -0
- package/dist/index-DaBcikv9.d.mts +118 -0
- package/dist/index-DlaMq1nb.d.mts +955 -0
- package/dist/index-DoXZYQpq.d.mts +290 -0
- package/dist/index-G6V0Z_Mn.d.mts +125 -0
- package/dist/index-z0KSvE5i.d.mts +246 -0
- package/dist/index-zjjNBRY2.d.mts +95 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +33 -0
- package/dist/integrations/next-js.d.mts +21 -0
- package/dist/integrations/next-js.mjs +62 -0
- package/dist/integrations/node.d.mts +15 -0
- package/dist/integrations/node.mjs +15 -0
- package/dist/integrations/react-start.d.mts +15 -0
- package/dist/integrations/react-start.mjs +46 -0
- package/dist/json-D0TBLy4q.mjs +24 -0
- package/dist/jwt-CmxZAGe2.mjs +94 -0
- package/dist/jwt-lHV4QHpD.mjs +387 -0
- package/dist/kysely-adapter-DpzYO8Yf.mjs +181 -0
- package/dist/magic-link-Bk-ueorR.mjs +176 -0
- package/dist/misc-beA1w3XV.mjs +7 -0
- package/dist/multi-session-DRrLKrg9.mjs +144 -0
- package/dist/node-sqlite-dialect-BcKxBQnm.mjs +155 -0
- package/dist/oauth-proxy-D1sWh9B_.mjs +133 -0
- package/dist/oauth2/index.d.mts +60 -0
- package/dist/oauth2/index.mjs +19 -0
- package/dist/one-tap-DMswkTEc.mjs +113 -0
- package/dist/organization-BArHO_5v.mjs +3857 -0
- package/dist/parser-DcrIEuXE.mjs +73 -0
- package/dist/passkey-CcD4CnhM.mjs +558 -0
- package/dist/password-sVC4nePO.mjs +25 -0
- package/dist/permission-DLYUF3cW.mjs +15 -0
- package/dist/phone-number-rHDIlh-2.mjs +485 -0
- package/dist/plugin-helper-4L-lzsjZ.mjs +16 -0
- package/dist/plugins/access/index.d.mts +3 -0
- package/dist/plugins/access/index.mjs +3 -0
- package/dist/plugins/admin/access/index.d.mts +116 -0
- package/dist/plugins/admin/access/index.mjs +4 -0
- package/dist/plugins/admin/index.d.mts +9 -0
- package/dist/plugins/admin/index.mjs +22 -0
- package/dist/plugins/anonymous/index.d.mts +7 -0
- package/dist/plugins/anonymous/index.mjs +18 -0
- package/dist/plugins/bearer/index.d.mts +2 -0
- package/dist/plugins/bearer/index.mjs +8 -0
- package/dist/plugins/captcha/index.d.mts +2 -0
- package/dist/plugins/captcha/index.mjs +4 -0
- package/dist/plugins/custom-session/index.d.mts +7 -0
- package/dist/plugins/custom-session/index.mjs +19 -0
- package/dist/plugins/device-authorization/index.d.mts +4 -0
- package/dist/plugins/device-authorization/index.mjs +18 -0
- package/dist/plugins/email-otp/index.d.mts +3 -0
- package/dist/plugins/email-otp/index.mjs +19 -0
- package/dist/plugins/generic-oauth/index.d.mts +7 -0
- package/dist/plugins/generic-oauth/index.mjs +18 -0
- package/dist/plugins/haveibeenpwned/index.d.mts +2 -0
- package/dist/plugins/haveibeenpwned/index.mjs +18 -0
- package/dist/plugins/index.d.mts +139 -0
- package/dist/plugins/index.mjs +47 -0
- package/dist/plugins/jwt/index.d.mts +7 -0
- package/dist/plugins/jwt/index.mjs +18 -0
- package/dist/plugins/magic-link/index.d.mts +2 -0
- package/dist/plugins/magic-link/index.mjs +18 -0
- package/dist/plugins/multi-session/index.d.mts +3 -0
- package/dist/plugins/multi-session/index.mjs +18 -0
- package/dist/plugins/oauth-proxy/index.d.mts +2 -0
- package/dist/plugins/oauth-proxy/index.mjs +18 -0
- package/dist/plugins/one-tap/index.d.mts +2 -0
- package/dist/plugins/one-tap/index.mjs +18 -0
- package/dist/plugins/one-time-token/index.d.mts +7 -0
- package/dist/plugins/one-time-token/index.mjs +47 -0
- package/dist/plugins/organization/access/index.d.mts +4 -0
- package/dist/plugins/organization/access/index.mjs +4 -0
- package/dist/plugins/organization/index.d.mts +9 -0
- package/dist/plugins/organization/index.mjs +22 -0
- package/dist/plugins/passkey/index.d.mts +7 -0
- package/dist/plugins/passkey/index.mjs +18 -0
- package/dist/plugins/phone-number/index.d.mts +7 -0
- package/dist/plugins/phone-number/index.mjs +18 -0
- package/dist/plugins/two-factor/index.d.mts +7 -0
- package/dist/plugins/two-factor/index.mjs +20 -0
- package/dist/plugins/username/index.d.mts +4 -0
- package/dist/plugins/username/index.mjs +18 -0
- package/dist/plugins-3jfNste3.mjs +1544 -0
- package/dist/plugins-hFZT3hmK.d.mts +14 -0
- package/dist/promise-8rrEHRXd.mjs +23154 -0
- package/dist/proxy-CL9PZXJA.mjs +441 -0
- package/dist/session-Dgp133Yt.mjs +413 -0
- package/dist/social-providers/index.d.mts +1 -0
- package/dist/social-providers/index.mjs +3 -0
- package/dist/src/adapters/adapter-factory/index.d.ts +10 -0
- package/dist/src/adapters/adapter-factory/types.d.ts +124 -0
- package/dist/src/adapters/create-test-suite.d.ts +97 -0
- package/dist/src/adapters/drizzle-adapter/drizzle-adapter.d.ts +43 -0
- package/dist/src/adapters/drizzle-adapter/index.d.ts +1 -0
- package/dist/src/adapters/drizzle-adapter/test/generate-schema.d.ts +12 -0
- package/dist/src/adapters/index.d.ts +21 -0
- package/dist/src/adapters/kysely-adapter/bun-sqlite-dialect.d.ts +58 -0
- package/dist/src/adapters/kysely-adapter/dialect.d.ts +13 -0
- package/dist/src/adapters/kysely-adapter/index.d.ts +3 -0
- package/dist/src/adapters/kysely-adapter/kysely-adapter.d.ts +32 -0
- package/dist/src/adapters/kysely-adapter/node-sqlite-dialect.d.ts +58 -0
- package/dist/src/adapters/kysely-adapter/types.d.ts +1 -0
- package/dist/src/adapters/memory-adapter/index.d.ts +1 -0
- package/dist/src/adapters/memory-adapter/memory-adapter.d.ts +9 -0
- package/dist/src/adapters/mongodb-adapter/index.d.ts +1 -0
- package/dist/src/adapters/mongodb-adapter/mongodb-adapter.d.ts +31 -0
- package/dist/src/adapters/prisma-adapter/index.d.ts +1 -0
- package/dist/src/adapters/prisma-adapter/prisma-adapter.d.ts +32 -0
- package/dist/src/adapters/prisma-adapter/test/auth.d.ts +3 -0
- package/dist/src/adapters/prisma-adapter/test/generate-auth-config.d.ts +2 -0
- package/dist/src/adapters/prisma-adapter/test/generate-prisma-schema.d.ts +3 -0
- package/dist/src/adapters/prisma-adapter/test/get-prisma-client.d.ts +7 -0
- package/dist/src/adapters/prisma-adapter/test/push-prisma-schema.d.ts +1 -0
- package/dist/src/adapters/test-adapter.d.ts +61 -0
- package/dist/src/adapters/test.d.ts +78 -0
- package/dist/src/adapters/tests/auth-flow.d.ts +22 -0
- package/dist/src/adapters/tests/index.d.ts +5 -0
- package/dist/src/adapters/tests/normal.d.ts +87 -0
- package/dist/src/adapters/tests/number-id.d.ts +17 -0
- package/dist/src/adapters/tests/performance.d.ts +24 -0
- package/dist/src/adapters/tests/transactions.d.ts +20 -0
- package/dist/src/adapters/utils.d.ts +3 -0
- package/dist/src/api/index.d.ts +7150 -0
- package/dist/src/api/middlewares/index.d.ts +2 -0
- package/dist/src/api/middlewares/oauth.d.ts +15 -0
- package/dist/src/api/middlewares/origin-check.d.ts +8 -0
- package/dist/src/api/rate-limiter/index.d.ts +13 -0
- package/dist/src/api/routes/account.d.ts +623 -0
- package/dist/src/api/routes/callback.d.ts +68 -0
- package/dist/src/api/routes/email-verification.d.ts +266 -0
- package/dist/src/api/routes/error.d.ts +51 -0
- package/dist/src/api/routes/index.d.ts +11 -0
- package/dist/src/api/routes/ok.d.ts +61 -0
- package/dist/src/api/routes/reset-password.d.ts +203 -0
- package/dist/src/api/routes/session.d.ts +586 -0
- package/dist/src/api/routes/sign-in.d.ts +298 -0
- package/dist/src/api/routes/sign-out.d.ts +59 -0
- package/dist/src/api/routes/sign-up.d.ts +625 -0
- package/dist/src/api/routes/update-user.d.ts +862 -0
- package/dist/src/api/to-auth-endpoints.d.ts +3 -0
- package/dist/src/auth.d.ts +25 -0
- package/dist/src/client/broadcast-channel.d.ts +12 -0
- package/dist/src/client/config.d.ts +85 -0
- package/dist/src/client/fetch-plugins.d.ts +14 -0
- package/dist/src/client/index.d.ts +15 -0
- package/dist/src/client/parser.d.ts +12 -0
- package/dist/src/client/path-to-object.d.ts +63 -0
- package/dist/src/client/plugins/index.d.ts +21 -0
- package/dist/src/client/plugins/infer-plugin.d.ts +11 -0
- package/dist/src/client/proxy.d.ts +8 -0
- package/dist/src/client/query.d.ts +16 -0
- package/dist/src/client/react/index.d.ts +130 -0
- package/dist/src/client/react/react-store.d.ts +43 -0
- package/dist/src/client/session-atom.d.ts +18 -0
- package/dist/src/client/session-refresh.d.ts +17 -0
- package/dist/src/client/test-plugin.d.ts +147 -0
- package/dist/src/client/types.d.ts +49 -0
- package/dist/src/client/vanilla.d.ts +124 -0
- package/dist/src/cookies/check-cookies.d.ts +4 -0
- package/dist/src/cookies/cookie-utils.d.ts +16 -0
- package/dist/src/cookies/index.d.ts +56 -0
- package/dist/src/cookies/session-store.d.ts +43 -0
- package/dist/src/crypto/buffer.d.ts +4 -0
- package/dist/src/crypto/index.d.ts +14 -0
- package/dist/src/crypto/jwt.d.ts +4 -0
- package/dist/src/crypto/password.d.ts +5 -0
- package/dist/src/crypto/random.d.ts +1 -0
- package/dist/src/db/field.d.ts +97 -0
- package/dist/src/db/get-migration.d.ts +18 -0
- package/dist/src/db/get-schema.d.ts +6 -0
- package/dist/src/db/get-tables.d.ts +3 -0
- package/dist/src/db/index.d.ts +10 -0
- package/dist/src/db/internal-adapter.d.ts +9 -0
- package/dist/src/db/schema.d.ts +76 -0
- package/dist/src/db/to-zod.d.ts +31 -0
- package/dist/src/db/utils.d.ts +6 -0
- package/dist/src/db/with-hooks.d.ts +27 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/init.d.ts +2 -0
- package/dist/src/integrations/next-js.d.ts +15 -0
- package/dist/src/integrations/node.d.ts +6 -0
- package/dist/src/integrations/react-start.d.ts +9 -0
- package/dist/src/oauth2/index.d.ts +4 -0
- package/dist/src/oauth2/link-account.d.ts +41 -0
- package/dist/src/oauth2/state.d.ts +21 -0
- package/dist/src/oauth2/utils.d.ts +3 -0
- package/dist/src/plugins/access/access.d.ts +25 -0
- package/dist/src/plugins/access/index.d.ts +2 -0
- package/dist/src/plugins/access/types.d.ts +14 -0
- package/dist/src/plugins/additional-fields/client.d.ts +91 -0
- package/dist/src/plugins/admin/access/index.d.ts +1 -0
- package/dist/src/plugins/admin/access/statement.d.ts +110 -0
- package/dist/src/plugins/admin/admin.d.ts +1570 -0
- package/dist/src/plugins/admin/client.d.ts +62 -0
- package/dist/src/plugins/admin/error-codes.d.ts +21 -0
- package/dist/src/plugins/admin/has-permission.d.ts +21 -0
- package/dist/src/plugins/admin/index.d.ts +2 -0
- package/dist/src/plugins/admin/schema.d.ts +36 -0
- package/dist/src/plugins/admin/types.d.ts +77 -0
- package/dist/src/plugins/anonymous/client.d.ts +8 -0
- package/dist/src/plugins/anonymous/index.d.ts +159 -0
- package/dist/src/plugins/api-key/client.d.ts +11 -0
- package/dist/src/plugins/api-key/index.d.ts +1431 -0
- package/dist/src/plugins/api-key/rate-limit.d.ts +20 -0
- package/dist/src/plugins/api-key/routes/create-api-key.d.ts +244 -0
- package/dist/src/plugins/api-key/routes/delete-all-expired-api-keys.d.ts +45 -0
- package/dist/src/plugins/api-key/routes/delete-api-key.d.ts +112 -0
- package/dist/src/plugins/api-key/routes/get-api-key.d.ts +234 -0
- package/dist/src/plugins/api-key/routes/index.d.ts +1136 -0
- package/dist/src/plugins/api-key/routes/list-api-keys.d.ts +231 -0
- package/dist/src/plugins/api-key/routes/update-api-key.d.ts +235 -0
- package/dist/src/plugins/api-key/routes/verify-api-key.d.ts +80 -0
- package/dist/src/plugins/api-key/schema.d.ts +188 -0
- package/dist/src/plugins/api-key/types.d.ts +285 -0
- package/dist/src/plugins/bearer/index.d.ts +31 -0
- package/dist/src/plugins/captcha/constants.d.ts +9 -0
- package/dist/src/plugins/captcha/error-codes.d.ts +9 -0
- package/dist/src/plugins/captcha/index.d.ts +7 -0
- package/dist/src/plugins/captcha/types.d.ts +23 -0
- package/dist/src/plugins/captcha/utils.d.ts +1 -0
- package/dist/src/plugins/captcha/verify-handlers/captchafox.d.ts +11 -0
- package/dist/src/plugins/captcha/verify-handlers/cloudflare-turnstile.d.ts +10 -0
- package/dist/src/plugins/captcha/verify-handlers/google-recaptcha.d.ts +11 -0
- package/dist/src/plugins/captcha/verify-handlers/h-captcha.d.ts +11 -0
- package/dist/src/plugins/captcha/verify-handlers/index.d.ts +4 -0
- package/dist/src/plugins/custom-session/client.d.ts +12 -0
- package/dist/src/plugins/custom-session/index.d.ts +89 -0
- package/dist/src/plugins/device-authorization/client.d.ts +12 -0
- package/dist/src/plugins/device-authorization/index.d.ts +568 -0
- package/dist/src/plugins/device-authorization/schema.d.ts +56 -0
- package/dist/src/plugins/email-otp/client.d.ts +9 -0
- package/dist/src/plugins/email-otp/index.d.ts +826 -0
- package/dist/src/plugins/email-otp/utils.d.ts +2 -0
- package/dist/src/plugins/generic-oauth/client.d.ts +5 -0
- package/dist/src/plugins/generic-oauth/index.d.ts +428 -0
- package/dist/src/plugins/haveibeenpwned/index.d.ts +25 -0
- package/dist/src/plugins/index.d.ts +28 -0
- package/dist/src/plugins/jwt/adapter.d.ts +8 -0
- package/dist/src/plugins/jwt/client.d.ts +5 -0
- package/dist/src/plugins/jwt/index.d.ts +276 -0
- package/dist/src/plugins/jwt/schema.d.ts +18 -0
- package/dist/src/plugins/jwt/sign.d.ts +8 -0
- package/dist/src/plugins/jwt/types.d.ts +175 -0
- package/dist/src/plugins/jwt/utils.d.ts +46 -0
- package/dist/src/plugins/last-login-method/client.d.ts +34 -0
- package/dist/src/plugins/last-login-method/index.d.ts +103 -0
- package/dist/src/plugins/magic-link/client.d.ts +5 -0
- package/dist/src/plugins/magic-link/index.d.ts +253 -0
- package/dist/src/plugins/magic-link/utils.d.ts +1 -0
- package/dist/src/plugins/multi-session/client.d.ts +9 -0
- package/dist/src/plugins/multi-session/index.d.ts +284 -0
- package/dist/src/plugins/oauth-proxy/index.d.ts +119 -0
- package/dist/src/plugins/one-tap/client.d.ts +81 -0
- package/dist/src/plugins/one-tap/index.d.ts +114 -0
- package/dist/src/plugins/one-time-token/client.d.ts +5 -0
- package/dist/src/plugins/one-time-token/index.d.ts +170 -0
- package/dist/src/plugins/one-time-token/utils.d.ts +1 -0
- package/dist/src/plugins/organization/access/index.d.ts +1 -0
- package/dist/src/plugins/organization/access/statement.d.ts +241 -0
- package/dist/src/plugins/organization/adapter.d.ts +685 -0
- package/dist/src/plugins/organization/call.d.ts +55 -0
- package/dist/src/plugins/organization/client.d.ts +344 -0
- package/dist/src/plugins/organization/error-codes.d.ts +58 -0
- package/dist/src/plugins/organization/has-permission.d.ts +13 -0
- package/dist/src/plugins/organization/index.d.ts +4 -0
- package/dist/src/plugins/organization/organization.d.ts +412 -0
- package/dist/src/plugins/organization/permission.d.ts +27 -0
- package/dist/src/plugins/organization/routes/crud-access-control.d.ts +701 -0
- package/dist/src/plugins/organization/routes/crud-invites.d.ts +1522 -0
- package/dist/src/plugins/organization/routes/crud-members.d.ts +1220 -0
- package/dist/src/plugins/organization/routes/crud-org.d.ts +1399 -0
- package/dist/src/plugins/organization/routes/crud-team.d.ts +1323 -0
- package/dist/src/plugins/organization/schema.d.ts +366 -0
- package/dist/src/plugins/organization/types.d.ts +737 -0
- package/dist/src/plugins/passkey/client.d.ts +190 -0
- package/dist/src/plugins/passkey/error-codes.d.ts +9 -0
- package/dist/src/plugins/passkey/index.d.ts +838 -0
- package/dist/src/plugins/passkey/schema.d.ts +50 -0
- package/dist/src/plugins/passkey/types.d.ts +69 -0
- package/dist/src/plugins/passkey/utils.d.ts +2 -0
- package/dist/src/plugins/phone-number/client.d.ts +9 -0
- package/dist/src/plugins/phone-number/index.d.ts +622 -0
- package/dist/src/plugins/phone-number/phone-number-error.d.ts +10 -0
- package/dist/src/plugins/two-factor/backup-codes/index.d.ts +387 -0
- package/dist/src/plugins/two-factor/client.d.ts +28 -0
- package/dist/src/plugins/two-factor/constant.d.ts +2 -0
- package/dist/src/plugins/two-factor/error-code.d.ts +11 -0
- package/dist/src/plugins/two-factor/index.d.ts +1014 -0
- package/dist/src/plugins/two-factor/otp/index.d.ts +282 -0
- package/dist/src/plugins/two-factor/schema.d.ts +35 -0
- package/dist/src/plugins/two-factor/totp/index.d.ts +298 -0
- package/dist/src/plugins/two-factor/types.d.ts +50 -0
- package/dist/src/plugins/two-factor/utils.d.ts +1 -0
- package/dist/src/plugins/two-factor/verify-two-factor.d.ts +59 -0
- package/dist/src/plugins/username/client.d.ts +5 -0
- package/dist/src/plugins/username/error-codes.d.ts +10 -0
- package/dist/src/plugins/username/index.d.ts +313 -0
- package/dist/src/plugins/username/schema.d.ts +27 -0
- package/dist/src/social-providers/index.d.ts +1 -0
- package/dist/src/test-utils/headers.d.ts +5 -0
- package/dist/src/test-utils/index.d.ts +2 -0
- package/dist/src/test-utils/test-instance.d.ts +17849 -0
- package/dist/src/types/adapter.d.ts +21 -0
- package/dist/src/types/api.d.ts +29 -0
- package/dist/src/types/helper.d.ts +32 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/models.d.ts +13 -0
- package/dist/src/types/plugins.d.ts +14 -0
- package/dist/src/utils/await-object.d.ts +3 -0
- package/dist/src/utils/boolean.d.ts +1 -0
- package/dist/src/utils/clone.d.ts +1 -0
- package/dist/src/utils/constants.d.ts +1 -0
- package/dist/src/utils/date.d.ts +1 -0
- package/dist/src/utils/ensure-utc.d.ts +1 -0
- package/dist/src/utils/get-request-ip.d.ts +2 -0
- package/dist/src/utils/hashing.d.ts +6 -0
- package/dist/src/utils/hide-metadata.d.ts +3 -0
- package/dist/src/utils/id.d.ts +1 -0
- package/dist/src/utils/import-util.d.ts +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/is-atom.d.ts +2 -0
- package/dist/src/utils/is-promise.d.ts +1 -0
- package/dist/src/utils/json.d.ts +1 -0
- package/dist/src/utils/merger.d.ts +1 -0
- package/dist/src/utils/middleware-response.d.ts +8 -0
- package/dist/src/utils/misc.d.ts +1 -0
- package/dist/src/utils/password.d.ts +6 -0
- package/dist/src/utils/plugin-helper.d.ts +5 -0
- package/dist/src/utils/shim.d.ts +3 -0
- package/dist/src/utils/time.d.ts +4 -0
- package/dist/src/utils/url.d.ts +4 -0
- package/dist/src/utils/wildcard.d.ts +45 -0
- package/dist/test-utils/index.d.mts +17870 -0
- package/dist/test-utils/index.mjs +272 -0
- package/dist/two-factor-ClUXxom2.mjs +914 -0
- package/dist/types/index.d.mts +6 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types-B1OL7hyf.d.mts +4 -0
- package/dist/types-Bjn15Uy6.mjs +1 -0
- package/dist/url-VB2gAbTI.mjs +59 -0
- package/dist/username-DiGklOGl.mjs +278 -0
- package/dist/utils-SOMB4Px5.mjs +132 -0
- package/package.json +447 -0
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
import { t as safeJSONParse } from "./json-D0TBLy4q.mjs";
|
|
2
|
+
import { n as symmetricEncrypt, r as generateRandomString, t as symmetricDecrypt } from "./crypto-Bb42eMrH.mjs";
|
|
3
|
+
import { c as setSessionCookie, n as deleteSessionCookie } from "./cookies-cJObLwn1.mjs";
|
|
4
|
+
import { n as mergeSchema } from "./date-Cx2yCiC4.mjs";
|
|
5
|
+
import { r as getSessionFromCtx, u as sessionMiddleware } from "./session-Dgp133Yt.mjs";
|
|
6
|
+
import { n as validatePassword } from "./password-sVC4nePO.mjs";
|
|
7
|
+
import { BASE_ERROR_CODES } from "@gatwech/core/error";
|
|
8
|
+
import { defineErrorCodes } from "@gatwech/core/utils";
|
|
9
|
+
import { APIError } from "better-call";
|
|
10
|
+
import { createAuthEndpoint, createAuthMiddleware } from "@gatwech/core/api";
|
|
11
|
+
import * as z$1 from "zod";
|
|
12
|
+
import { createHash } from "@gatwech/utils/hash";
|
|
13
|
+
import { base64Url } from "@gatwech/utils/base64";
|
|
14
|
+
import { createHMAC } from "@gatwech/utils/hmac";
|
|
15
|
+
import { createOTP } from "@gatwech/utils/otp";
|
|
16
|
+
|
|
17
|
+
//#region src/plugins/two-factor/error-code.ts
|
|
18
|
+
const TWO_FACTOR_ERROR_CODES = defineErrorCodes({
|
|
19
|
+
OTP_NOT_ENABLED: "OTP not enabled",
|
|
20
|
+
OTP_HAS_EXPIRED: "OTP has expired",
|
|
21
|
+
TOTP_NOT_ENABLED: "TOTP not enabled",
|
|
22
|
+
TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled",
|
|
23
|
+
BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled",
|
|
24
|
+
INVALID_BACKUP_CODE: "Invalid backup code",
|
|
25
|
+
INVALID_CODE: "Invalid code",
|
|
26
|
+
TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.",
|
|
27
|
+
INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie"
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/plugins/two-factor/constant.ts
|
|
32
|
+
const TWO_FACTOR_COOKIE_NAME = "two_factor";
|
|
33
|
+
const TRUST_DEVICE_COOKIE_NAME = "trust_device";
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/plugins/two-factor/verify-two-factor.ts
|
|
37
|
+
async function verifyTwoFactor(ctx) {
|
|
38
|
+
const session = await getSessionFromCtx(ctx);
|
|
39
|
+
if (!session) {
|
|
40
|
+
const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
|
|
41
|
+
const twoFactorCookie = await ctx.getSignedCookie(cookieName.name, ctx.context.secret);
|
|
42
|
+
if (!twoFactorCookie) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
|
|
43
|
+
const verificationToken = await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
|
|
44
|
+
if (!verificationToken) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
|
|
45
|
+
const user = await ctx.context.internalAdapter.findUserById(verificationToken.value);
|
|
46
|
+
if (!user) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
|
|
47
|
+
const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret);
|
|
48
|
+
return {
|
|
49
|
+
valid: async (ctx$1) => {
|
|
50
|
+
const session$1 = await ctx$1.context.internalAdapter.createSession(verificationToken.value, !!dontRememberMe);
|
|
51
|
+
if (!session$1) throw new APIError("INTERNAL_SERVER_ERROR", { message: "failed to create session" });
|
|
52
|
+
await setSessionCookie(ctx$1, {
|
|
53
|
+
session: session$1,
|
|
54
|
+
user
|
|
55
|
+
});
|
|
56
|
+
if (ctx$1.body.trustDevice) {
|
|
57
|
+
const trustDeviceCookie = ctx$1.context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME, { maxAge: 720 * 60 * 60 });
|
|
58
|
+
/**
|
|
59
|
+
* create a token that will be used to
|
|
60
|
+
* verify the device
|
|
61
|
+
*/
|
|
62
|
+
const token = await createHMAC("SHA-256", "base64urlnopad").sign(ctx$1.context.secret, `${user.id}!${session$1.token}`);
|
|
63
|
+
await ctx$1.setSignedCookie(trustDeviceCookie.name, `${token}!${session$1.token}`, ctx$1.context.secret, trustDeviceCookie.attributes);
|
|
64
|
+
ctx$1.setCookie(ctx$1.context.authCookies.dontRememberToken.name, "", { maxAge: 0 });
|
|
65
|
+
ctx$1.setCookie(cookieName.name, "", { maxAge: 0 });
|
|
66
|
+
}
|
|
67
|
+
return ctx$1.json({
|
|
68
|
+
token: session$1.token,
|
|
69
|
+
user: {
|
|
70
|
+
id: user.id,
|
|
71
|
+
email: user.email,
|
|
72
|
+
emailVerified: user.emailVerified,
|
|
73
|
+
name: user.name,
|
|
74
|
+
image: user.image,
|
|
75
|
+
createdAt: user.createdAt,
|
|
76
|
+
updatedAt: user.updatedAt
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
invalid: async (errorKey) => {
|
|
81
|
+
throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES[errorKey] });
|
|
82
|
+
},
|
|
83
|
+
session: {
|
|
84
|
+
session: null,
|
|
85
|
+
user
|
|
86
|
+
},
|
|
87
|
+
key: twoFactorCookie
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
valid: async (ctx$1) => {
|
|
92
|
+
return ctx$1.json({
|
|
93
|
+
token: session.session.token,
|
|
94
|
+
user: {
|
|
95
|
+
id: session.user.id,
|
|
96
|
+
email: session.user.email,
|
|
97
|
+
emailVerified: session.user.emailVerified,
|
|
98
|
+
name: session.user.name,
|
|
99
|
+
image: session.user.image,
|
|
100
|
+
createdAt: session.user.createdAt,
|
|
101
|
+
updatedAt: session.user.updatedAt
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
invalid: async () => {
|
|
106
|
+
throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
|
|
107
|
+
},
|
|
108
|
+
session,
|
|
109
|
+
key: `${session.user.id}!${session.session.id}`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/plugins/two-factor/backup-codes/index.ts
|
|
115
|
+
function generateBackupCodesFn(options) {
|
|
116
|
+
return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => generateRandomString(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
|
|
117
|
+
}
|
|
118
|
+
async function generateBackupCodes(secret, options) {
|
|
119
|
+
const backupCodes = options?.customBackupCodesGenerate ? options.customBackupCodesGenerate() : generateBackupCodesFn(options);
|
|
120
|
+
if (options?.storeBackupCodes === "encrypted") return {
|
|
121
|
+
backupCodes,
|
|
122
|
+
encryptedBackupCodes: await symmetricEncrypt({
|
|
123
|
+
data: JSON.stringify(backupCodes),
|
|
124
|
+
key: secret
|
|
125
|
+
})
|
|
126
|
+
};
|
|
127
|
+
if (typeof options?.storeBackupCodes === "object" && "encrypt" in options?.storeBackupCodes) return {
|
|
128
|
+
backupCodes,
|
|
129
|
+
encryptedBackupCodes: await options?.storeBackupCodes.encrypt(JSON.stringify(backupCodes))
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
backupCodes,
|
|
133
|
+
encryptedBackupCodes: JSON.stringify(backupCodes)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async function verifyBackupCode(data, key, options) {
|
|
137
|
+
const codes = await getBackupCodes(data.backupCodes, key, options);
|
|
138
|
+
if (!codes) return {
|
|
139
|
+
status: false,
|
|
140
|
+
updated: null
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
status: codes.includes(data.code),
|
|
144
|
+
updated: codes.filter((code) => code !== data.code)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async function getBackupCodes(backupCodes, key, options) {
|
|
148
|
+
if (options?.storeBackupCodes === "encrypted") return safeJSONParse(await symmetricDecrypt({
|
|
149
|
+
key,
|
|
150
|
+
data: backupCodes
|
|
151
|
+
}));
|
|
152
|
+
if (typeof options?.storeBackupCodes === "object" && "decrypt" in options?.storeBackupCodes) return safeJSONParse(await options?.storeBackupCodes.decrypt(backupCodes));
|
|
153
|
+
return safeJSONParse(backupCodes);
|
|
154
|
+
}
|
|
155
|
+
const backupCode2fa = (opts) => {
|
|
156
|
+
const twoFactorTable = "twoFactor";
|
|
157
|
+
return {
|
|
158
|
+
id: "backup_code",
|
|
159
|
+
endpoints: {
|
|
160
|
+
verifyBackupCode: createAuthEndpoint("/two-factor/verify-backup-code", {
|
|
161
|
+
method: "POST",
|
|
162
|
+
body: z$1.object({
|
|
163
|
+
code: z$1.string().meta({ description: `A backup code to verify. Eg: "123456"` }),
|
|
164
|
+
disableSession: z$1.boolean().meta({ description: "If true, the session cookie will not be set." }).optional(),
|
|
165
|
+
trustDevice: z$1.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional()
|
|
166
|
+
}),
|
|
167
|
+
metadata: { openapi: {
|
|
168
|
+
description: "Verify a backup code for two-factor authentication",
|
|
169
|
+
responses: { "200": {
|
|
170
|
+
description: "Backup code verified successfully",
|
|
171
|
+
content: { "application/json": { schema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
user: {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: {
|
|
177
|
+
id: {
|
|
178
|
+
type: "string",
|
|
179
|
+
description: "Unique identifier of the user"
|
|
180
|
+
},
|
|
181
|
+
email: {
|
|
182
|
+
type: "string",
|
|
183
|
+
format: "email",
|
|
184
|
+
nullable: true,
|
|
185
|
+
description: "User's email address"
|
|
186
|
+
},
|
|
187
|
+
emailVerified: {
|
|
188
|
+
type: "boolean",
|
|
189
|
+
nullable: true,
|
|
190
|
+
description: "Whether the email is verified"
|
|
191
|
+
},
|
|
192
|
+
name: {
|
|
193
|
+
type: "string",
|
|
194
|
+
nullable: true,
|
|
195
|
+
description: "User's name"
|
|
196
|
+
},
|
|
197
|
+
image: {
|
|
198
|
+
type: "string",
|
|
199
|
+
format: "uri",
|
|
200
|
+
nullable: true,
|
|
201
|
+
description: "User's profile image URL"
|
|
202
|
+
},
|
|
203
|
+
twoFactorEnabled: {
|
|
204
|
+
type: "boolean",
|
|
205
|
+
description: "Whether two-factor authentication is enabled for the user"
|
|
206
|
+
},
|
|
207
|
+
createdAt: {
|
|
208
|
+
type: "string",
|
|
209
|
+
format: "date-time",
|
|
210
|
+
description: "Timestamp when the user was created"
|
|
211
|
+
},
|
|
212
|
+
updatedAt: {
|
|
213
|
+
type: "string",
|
|
214
|
+
format: "date-time",
|
|
215
|
+
description: "Timestamp when the user was last updated"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
required: [
|
|
219
|
+
"id",
|
|
220
|
+
"twoFactorEnabled",
|
|
221
|
+
"createdAt",
|
|
222
|
+
"updatedAt"
|
|
223
|
+
],
|
|
224
|
+
description: "The authenticated user object with two-factor details"
|
|
225
|
+
},
|
|
226
|
+
session: {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: {
|
|
229
|
+
token: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: "Session token"
|
|
232
|
+
},
|
|
233
|
+
userId: {
|
|
234
|
+
type: "string",
|
|
235
|
+
description: "ID of the user associated with the session"
|
|
236
|
+
},
|
|
237
|
+
createdAt: {
|
|
238
|
+
type: "string",
|
|
239
|
+
format: "date-time",
|
|
240
|
+
description: "Timestamp when the session was created"
|
|
241
|
+
},
|
|
242
|
+
expiresAt: {
|
|
243
|
+
type: "string",
|
|
244
|
+
format: "date-time",
|
|
245
|
+
description: "Timestamp when the session expires"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
required: [
|
|
249
|
+
"token",
|
|
250
|
+
"userId",
|
|
251
|
+
"createdAt",
|
|
252
|
+
"expiresAt"
|
|
253
|
+
],
|
|
254
|
+
description: "The current session object, included unless disableSession is true"
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
required: ["user", "session"]
|
|
258
|
+
} } }
|
|
259
|
+
} }
|
|
260
|
+
} }
|
|
261
|
+
}, async (ctx) => {
|
|
262
|
+
const { session, valid } = await verifyTwoFactor(ctx);
|
|
263
|
+
const user = session.user;
|
|
264
|
+
const twoFactor$1 = await ctx.context.adapter.findOne({
|
|
265
|
+
model: twoFactorTable,
|
|
266
|
+
where: [{
|
|
267
|
+
field: "userId",
|
|
268
|
+
value: user.id
|
|
269
|
+
}]
|
|
270
|
+
});
|
|
271
|
+
if (!twoFactor$1) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED });
|
|
272
|
+
const validate = await verifyBackupCode({
|
|
273
|
+
backupCodes: twoFactor$1.backupCodes,
|
|
274
|
+
code: ctx.body.code
|
|
275
|
+
}, ctx.context.secret, opts);
|
|
276
|
+
if (!validate.status) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE });
|
|
277
|
+
const updatedBackupCodes = await symmetricEncrypt({
|
|
278
|
+
key: ctx.context.secret,
|
|
279
|
+
data: JSON.stringify(validate.updated)
|
|
280
|
+
});
|
|
281
|
+
await ctx.context.adapter.updateMany({
|
|
282
|
+
model: twoFactorTable,
|
|
283
|
+
update: { backupCodes: updatedBackupCodes },
|
|
284
|
+
where: [{
|
|
285
|
+
field: "userId",
|
|
286
|
+
value: user.id
|
|
287
|
+
}]
|
|
288
|
+
});
|
|
289
|
+
if (!ctx.body.disableSession) return valid(ctx);
|
|
290
|
+
return ctx.json({
|
|
291
|
+
token: session.session?.token,
|
|
292
|
+
user: {
|
|
293
|
+
id: session.user?.id,
|
|
294
|
+
email: session.user.email,
|
|
295
|
+
emailVerified: session.user.emailVerified,
|
|
296
|
+
name: session.user.name,
|
|
297
|
+
image: session.user.image,
|
|
298
|
+
createdAt: session.user.createdAt,
|
|
299
|
+
updatedAt: session.user.updatedAt
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}),
|
|
303
|
+
generateBackupCodes: createAuthEndpoint("/two-factor/generate-backup-codes", {
|
|
304
|
+
method: "POST",
|
|
305
|
+
body: z$1.object({ password: z$1.string().meta({ description: "The users password." }) }),
|
|
306
|
+
use: [sessionMiddleware],
|
|
307
|
+
metadata: { openapi: {
|
|
308
|
+
description: "Generate new backup codes for two-factor authentication",
|
|
309
|
+
responses: { "200": {
|
|
310
|
+
description: "Backup codes generated successfully",
|
|
311
|
+
content: { "application/json": { schema: {
|
|
312
|
+
type: "object",
|
|
313
|
+
properties: {
|
|
314
|
+
status: {
|
|
315
|
+
type: "boolean",
|
|
316
|
+
description: "Indicates if the backup codes were generated successfully",
|
|
317
|
+
enum: [true]
|
|
318
|
+
},
|
|
319
|
+
backupCodes: {
|
|
320
|
+
type: "array",
|
|
321
|
+
items: { type: "string" },
|
|
322
|
+
description: "Array of generated backup codes in plain text"
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
required: ["status", "backupCodes"]
|
|
326
|
+
} } }
|
|
327
|
+
} }
|
|
328
|
+
} }
|
|
329
|
+
}, async (ctx) => {
|
|
330
|
+
const user = ctx.context.session.user;
|
|
331
|
+
if (!user.twoFactorEnabled) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.TWO_FACTOR_NOT_ENABLED });
|
|
332
|
+
await ctx.context.password.checkPassword(user.id, ctx);
|
|
333
|
+
const backupCodes = await generateBackupCodes(ctx.context.secret, opts);
|
|
334
|
+
await ctx.context.adapter.updateMany({
|
|
335
|
+
model: twoFactorTable,
|
|
336
|
+
update: { backupCodes: backupCodes.encryptedBackupCodes },
|
|
337
|
+
where: [{
|
|
338
|
+
field: "userId",
|
|
339
|
+
value: ctx.context.session.user.id
|
|
340
|
+
}]
|
|
341
|
+
});
|
|
342
|
+
return ctx.json({
|
|
343
|
+
status: true,
|
|
344
|
+
backupCodes: backupCodes.backupCodes
|
|
345
|
+
});
|
|
346
|
+
}),
|
|
347
|
+
viewBackupCodes: createAuthEndpoint("/two-factor/view-backup-codes", {
|
|
348
|
+
method: "POST",
|
|
349
|
+
body: z$1.object({ userId: z$1.coerce.string().meta({ description: `The user ID to view all backup codes. Eg: "user-id"` }) }),
|
|
350
|
+
metadata: { SERVER_ONLY: true }
|
|
351
|
+
}, async (ctx) => {
|
|
352
|
+
const twoFactor$1 = await ctx.context.adapter.findOne({
|
|
353
|
+
model: twoFactorTable,
|
|
354
|
+
where: [{
|
|
355
|
+
field: "userId",
|
|
356
|
+
value: ctx.body.userId
|
|
357
|
+
}]
|
|
358
|
+
});
|
|
359
|
+
if (!twoFactor$1) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED });
|
|
360
|
+
const decryptedBackupCodes = await getBackupCodes(twoFactor$1.backupCodes, ctx.context.secret, opts);
|
|
361
|
+
if (!decryptedBackupCodes) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE });
|
|
362
|
+
return ctx.json({
|
|
363
|
+
status: true,
|
|
364
|
+
backupCodes: decryptedBackupCodes
|
|
365
|
+
});
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/plugins/two-factor/utils.ts
|
|
373
|
+
const defaultKeyHasher = async (token) => {
|
|
374
|
+
const hash = await createHash("SHA-256", "none").digest(new TextEncoder().encode(token));
|
|
375
|
+
return base64Url.encode(new Uint8Array(hash), { padding: false });
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/plugins/two-factor/otp/index.ts
|
|
380
|
+
/**
|
|
381
|
+
* The otp adapter is created from the totp adapter.
|
|
382
|
+
*/
|
|
383
|
+
const otp2fa = (options) => {
|
|
384
|
+
const opts = {
|
|
385
|
+
storeOTP: "plain",
|
|
386
|
+
digits: 6,
|
|
387
|
+
...options,
|
|
388
|
+
period: (options?.period || 3) * 60 * 1e3
|
|
389
|
+
};
|
|
390
|
+
async function storeOTP(ctx, otp) {
|
|
391
|
+
if (opts.storeOTP === "hashed") return await defaultKeyHasher(otp);
|
|
392
|
+
if (typeof opts.storeOTP === "object" && "hash" in opts.storeOTP) return await opts.storeOTP.hash(otp);
|
|
393
|
+
if (typeof opts.storeOTP === "object" && "encrypt" in opts.storeOTP) return await opts.storeOTP.encrypt(otp);
|
|
394
|
+
if (opts.storeOTP === "encrypted") return await symmetricEncrypt({
|
|
395
|
+
key: ctx.context.secret,
|
|
396
|
+
data: otp
|
|
397
|
+
});
|
|
398
|
+
return otp;
|
|
399
|
+
}
|
|
400
|
+
async function decryptOTP(ctx, otp) {
|
|
401
|
+
if (opts.storeOTP === "hashed") return await defaultKeyHasher(otp);
|
|
402
|
+
if (opts.storeOTP === "encrypted") return await symmetricDecrypt({
|
|
403
|
+
key: ctx.context.secret,
|
|
404
|
+
data: otp
|
|
405
|
+
});
|
|
406
|
+
if (typeof opts.storeOTP === "object" && "encrypt" in opts.storeOTP) return await opts.storeOTP.decrypt(otp);
|
|
407
|
+
if (typeof opts.storeOTP === "object" && "hash" in opts.storeOTP) return await opts.storeOTP.hash(otp);
|
|
408
|
+
return otp;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
id: "otp",
|
|
412
|
+
endpoints: {
|
|
413
|
+
sendTwoFactorOTP: createAuthEndpoint("/two-factor/send-otp", {
|
|
414
|
+
method: "POST",
|
|
415
|
+
body: z$1.object({ trustDevice: z$1.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) }).optional(),
|
|
416
|
+
metadata: { openapi: {
|
|
417
|
+
summary: "Send two factor OTP",
|
|
418
|
+
description: "Send two factor OTP to the user",
|
|
419
|
+
responses: { 200: {
|
|
420
|
+
description: "Successful response",
|
|
421
|
+
content: { "application/json": { schema: {
|
|
422
|
+
type: "object",
|
|
423
|
+
properties: { status: { type: "boolean" } }
|
|
424
|
+
} } }
|
|
425
|
+
} }
|
|
426
|
+
} }
|
|
427
|
+
}, async (ctx) => {
|
|
428
|
+
if (!options || !options.sendOTP) {
|
|
429
|
+
ctx.context.logger.error("send otp isn't configured. Please configure the send otp function on otp options.");
|
|
430
|
+
throw new APIError("BAD_REQUEST", { message: "otp isn't configured" });
|
|
431
|
+
}
|
|
432
|
+
const { session, key } = await verifyTwoFactor(ctx);
|
|
433
|
+
if (!session.user.twoFactorEnabled) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED });
|
|
434
|
+
const code = generateRandomString(opts.digits, "0-9");
|
|
435
|
+
const hashedCode = await storeOTP(ctx, code);
|
|
436
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
437
|
+
value: `${hashedCode}:0`,
|
|
438
|
+
identifier: `2fa-otp-${key}`,
|
|
439
|
+
expiresAt: new Date(Date.now() + opts.period)
|
|
440
|
+
});
|
|
441
|
+
await options.sendOTP({
|
|
442
|
+
user: session.user,
|
|
443
|
+
otp: code
|
|
444
|
+
}, ctx.request);
|
|
445
|
+
return ctx.json({ status: true });
|
|
446
|
+
}),
|
|
447
|
+
verifyTwoFactorOTP: createAuthEndpoint("/two-factor/verify-otp", {
|
|
448
|
+
method: "POST",
|
|
449
|
+
body: z$1.object({
|
|
450
|
+
code: z$1.string().meta({ description: "The otp code to verify. Eg: \"012345\"" }),
|
|
451
|
+
trustDevice: z$1.boolean().optional().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" })
|
|
452
|
+
}),
|
|
453
|
+
metadata: { openapi: {
|
|
454
|
+
summary: "Verify two factor OTP",
|
|
455
|
+
description: "Verify two factor OTP",
|
|
456
|
+
responses: { "200": {
|
|
457
|
+
description: "Two-factor OTP verified successfully",
|
|
458
|
+
content: { "application/json": { schema: {
|
|
459
|
+
type: "object",
|
|
460
|
+
properties: {
|
|
461
|
+
token: {
|
|
462
|
+
type: "string",
|
|
463
|
+
description: "Session token for the authenticated session"
|
|
464
|
+
},
|
|
465
|
+
user: {
|
|
466
|
+
type: "object",
|
|
467
|
+
properties: {
|
|
468
|
+
id: {
|
|
469
|
+
type: "string",
|
|
470
|
+
description: "Unique identifier of the user"
|
|
471
|
+
},
|
|
472
|
+
email: {
|
|
473
|
+
type: "string",
|
|
474
|
+
format: "email",
|
|
475
|
+
nullable: true,
|
|
476
|
+
description: "User's email address"
|
|
477
|
+
},
|
|
478
|
+
emailVerified: {
|
|
479
|
+
type: "boolean",
|
|
480
|
+
nullable: true,
|
|
481
|
+
description: "Whether the email is verified"
|
|
482
|
+
},
|
|
483
|
+
name: {
|
|
484
|
+
type: "string",
|
|
485
|
+
nullable: true,
|
|
486
|
+
description: "User's name"
|
|
487
|
+
},
|
|
488
|
+
image: {
|
|
489
|
+
type: "string",
|
|
490
|
+
format: "uri",
|
|
491
|
+
nullable: true,
|
|
492
|
+
description: "User's profile image URL"
|
|
493
|
+
},
|
|
494
|
+
createdAt: {
|
|
495
|
+
type: "string",
|
|
496
|
+
format: "date-time",
|
|
497
|
+
description: "Timestamp when the user was created"
|
|
498
|
+
},
|
|
499
|
+
updatedAt: {
|
|
500
|
+
type: "string",
|
|
501
|
+
format: "date-time",
|
|
502
|
+
description: "Timestamp when the user was last updated"
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
required: [
|
|
506
|
+
"id",
|
|
507
|
+
"createdAt",
|
|
508
|
+
"updatedAt"
|
|
509
|
+
],
|
|
510
|
+
description: "The authenticated user object"
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
required: ["token", "user"]
|
|
514
|
+
} } }
|
|
515
|
+
} }
|
|
516
|
+
} }
|
|
517
|
+
}, async (ctx) => {
|
|
518
|
+
const { session, key, valid, invalid } = await verifyTwoFactor(ctx);
|
|
519
|
+
const toCheckOtp = await ctx.context.internalAdapter.findVerificationValue(`2fa-otp-${key}`);
|
|
520
|
+
const [otp, counter] = toCheckOtp?.value?.split(":") ?? [];
|
|
521
|
+
const decryptedOtp = await decryptOTP(ctx, otp);
|
|
522
|
+
if (!toCheckOtp || toCheckOtp.expiresAt < /* @__PURE__ */ new Date()) {
|
|
523
|
+
if (toCheckOtp) await ctx.context.internalAdapter.deleteVerificationValue(toCheckOtp.id);
|
|
524
|
+
throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED });
|
|
525
|
+
}
|
|
526
|
+
const allowedAttempts = options?.allowedAttempts || 5;
|
|
527
|
+
if (parseInt(counter) >= allowedAttempts) {
|
|
528
|
+
await ctx.context.internalAdapter.deleteVerificationValue(toCheckOtp.id);
|
|
529
|
+
throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE });
|
|
530
|
+
}
|
|
531
|
+
if (decryptedOtp === ctx.body.code) {
|
|
532
|
+
if (!session.user.twoFactorEnabled) {
|
|
533
|
+
if (!session.session) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION });
|
|
534
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { twoFactorEnabled: true });
|
|
535
|
+
const newSession = await ctx.context.internalAdapter.createSession(session.user.id, false, session.session);
|
|
536
|
+
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
537
|
+
await setSessionCookie(ctx, {
|
|
538
|
+
session: newSession,
|
|
539
|
+
user: updatedUser
|
|
540
|
+
});
|
|
541
|
+
return ctx.json({
|
|
542
|
+
token: newSession.token,
|
|
543
|
+
user: {
|
|
544
|
+
id: updatedUser.id,
|
|
545
|
+
email: updatedUser.email,
|
|
546
|
+
emailVerified: updatedUser.emailVerified,
|
|
547
|
+
name: updatedUser.name,
|
|
548
|
+
image: updatedUser.image,
|
|
549
|
+
createdAt: updatedUser.createdAt,
|
|
550
|
+
updatedAt: updatedUser.updatedAt
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
return valid(ctx);
|
|
555
|
+
} else {
|
|
556
|
+
await ctx.context.internalAdapter.updateVerificationValue(toCheckOtp.id, { value: `${otp}:${(parseInt(counter, 10) || 0) + 1}` });
|
|
557
|
+
return invalid("INVALID_CODE");
|
|
558
|
+
}
|
|
559
|
+
})
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/plugins/two-factor/schema.ts
|
|
566
|
+
const schema = {
|
|
567
|
+
user: { fields: { twoFactorEnabled: {
|
|
568
|
+
type: "boolean",
|
|
569
|
+
required: false,
|
|
570
|
+
defaultValue: false,
|
|
571
|
+
input: false
|
|
572
|
+
} } },
|
|
573
|
+
twoFactor: { fields: {
|
|
574
|
+
secret: {
|
|
575
|
+
type: "string",
|
|
576
|
+
required: true,
|
|
577
|
+
returned: false
|
|
578
|
+
},
|
|
579
|
+
backupCodes: {
|
|
580
|
+
type: "string",
|
|
581
|
+
required: true,
|
|
582
|
+
returned: false
|
|
583
|
+
},
|
|
584
|
+
userId: {
|
|
585
|
+
type: "string",
|
|
586
|
+
required: true,
|
|
587
|
+
returned: false,
|
|
588
|
+
references: {
|
|
589
|
+
model: "user",
|
|
590
|
+
field: "id"
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} }
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region src/plugins/two-factor/totp/index.ts
|
|
598
|
+
const totp2fa = (options) => {
|
|
599
|
+
const opts = {
|
|
600
|
+
...options,
|
|
601
|
+
digits: options?.digits || 6,
|
|
602
|
+
period: options?.period || 30
|
|
603
|
+
};
|
|
604
|
+
const twoFactorTable = "twoFactor";
|
|
605
|
+
return {
|
|
606
|
+
id: "totp",
|
|
607
|
+
endpoints: {
|
|
608
|
+
generateTOTP: createAuthEndpoint("/totp/generate", {
|
|
609
|
+
method: "POST",
|
|
610
|
+
body: z$1.object({ secret: z$1.string().meta({ description: "The secret to generate the TOTP code" }) }),
|
|
611
|
+
metadata: {
|
|
612
|
+
openapi: {
|
|
613
|
+
summary: "Generate TOTP code",
|
|
614
|
+
description: "Use this endpoint to generate a TOTP code",
|
|
615
|
+
responses: { 200: {
|
|
616
|
+
description: "Successful response",
|
|
617
|
+
content: { "application/json": { schema: {
|
|
618
|
+
type: "object",
|
|
619
|
+
properties: { code: { type: "string" } }
|
|
620
|
+
} } }
|
|
621
|
+
} }
|
|
622
|
+
},
|
|
623
|
+
SERVER_ONLY: true
|
|
624
|
+
}
|
|
625
|
+
}, async (ctx) => {
|
|
626
|
+
if (options?.disable) {
|
|
627
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
628
|
+
throw new APIError("BAD_REQUEST", { message: "totp isn't configured" });
|
|
629
|
+
}
|
|
630
|
+
return { code: await createOTP(ctx.body.secret, {
|
|
631
|
+
period: opts.period,
|
|
632
|
+
digits: opts.digits
|
|
633
|
+
}).totp() };
|
|
634
|
+
}),
|
|
635
|
+
getTOTPURI: createAuthEndpoint("/two-factor/get-totp-uri", {
|
|
636
|
+
method: "POST",
|
|
637
|
+
use: [sessionMiddleware],
|
|
638
|
+
body: z$1.object({ password: z$1.string().meta({ description: "User password" }) }),
|
|
639
|
+
metadata: { openapi: {
|
|
640
|
+
summary: "Get TOTP URI",
|
|
641
|
+
description: "Use this endpoint to get the TOTP URI",
|
|
642
|
+
responses: { 200: {
|
|
643
|
+
description: "Successful response",
|
|
644
|
+
content: { "application/json": { schema: {
|
|
645
|
+
type: "object",
|
|
646
|
+
properties: { totpURI: { type: "string" } }
|
|
647
|
+
} } }
|
|
648
|
+
} }
|
|
649
|
+
} }
|
|
650
|
+
}, async (ctx) => {
|
|
651
|
+
if (options?.disable) {
|
|
652
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
653
|
+
throw new APIError("BAD_REQUEST", { message: "totp isn't configured" });
|
|
654
|
+
}
|
|
655
|
+
const user = ctx.context.session.user;
|
|
656
|
+
const twoFactor$1 = await ctx.context.adapter.findOne({
|
|
657
|
+
model: twoFactorTable,
|
|
658
|
+
where: [{
|
|
659
|
+
field: "userId",
|
|
660
|
+
value: user.id
|
|
661
|
+
}]
|
|
662
|
+
});
|
|
663
|
+
if (!twoFactor$1) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED });
|
|
664
|
+
const secret = await symmetricDecrypt({
|
|
665
|
+
key: ctx.context.secret,
|
|
666
|
+
data: twoFactor$1.secret
|
|
667
|
+
});
|
|
668
|
+
await ctx.context.password.checkPassword(user.id, ctx);
|
|
669
|
+
return { totpURI: createOTP(secret, {
|
|
670
|
+
digits: opts.digits,
|
|
671
|
+
period: opts.period
|
|
672
|
+
}).url(options?.issuer || ctx.context.appName, user.email) };
|
|
673
|
+
}),
|
|
674
|
+
verifyTOTP: createAuthEndpoint("/two-factor/verify-totp", {
|
|
675
|
+
method: "POST",
|
|
676
|
+
body: z$1.object({
|
|
677
|
+
code: z$1.string().meta({ description: "The otp code to verify. Eg: \"012345\"" }),
|
|
678
|
+
trustDevice: z$1.boolean().meta({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional()
|
|
679
|
+
}),
|
|
680
|
+
metadata: { openapi: {
|
|
681
|
+
summary: "Verify two factor TOTP",
|
|
682
|
+
description: "Verify two factor TOTP",
|
|
683
|
+
responses: { 200: {
|
|
684
|
+
description: "Successful response",
|
|
685
|
+
content: { "application/json": { schema: {
|
|
686
|
+
type: "object",
|
|
687
|
+
properties: { status: { type: "boolean" } }
|
|
688
|
+
} } }
|
|
689
|
+
} }
|
|
690
|
+
} }
|
|
691
|
+
}, async (ctx) => {
|
|
692
|
+
if (options?.disable) {
|
|
693
|
+
ctx.context.logger.error("totp isn't configured. please pass totp option on two factor plugin to enable totp");
|
|
694
|
+
throw new APIError("BAD_REQUEST", { message: "totp isn't configured" });
|
|
695
|
+
}
|
|
696
|
+
const { session, valid, invalid } = await verifyTwoFactor(ctx);
|
|
697
|
+
const user = session.user;
|
|
698
|
+
const twoFactor$1 = await ctx.context.adapter.findOne({
|
|
699
|
+
model: twoFactorTable,
|
|
700
|
+
where: [{
|
|
701
|
+
field: "userId",
|
|
702
|
+
value: user.id
|
|
703
|
+
}]
|
|
704
|
+
});
|
|
705
|
+
if (!twoFactor$1) throw new APIError("BAD_REQUEST", { message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED });
|
|
706
|
+
if (!await createOTP(await symmetricDecrypt({
|
|
707
|
+
key: ctx.context.secret,
|
|
708
|
+
data: twoFactor$1.secret
|
|
709
|
+
}), {
|
|
710
|
+
period: opts.period,
|
|
711
|
+
digits: opts.digits
|
|
712
|
+
}).verify(ctx.body.code, {})) return invalid("INVALID_CODE");
|
|
713
|
+
if (!user.twoFactorEnabled) {
|
|
714
|
+
if (!session.session) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION });
|
|
715
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: true });
|
|
716
|
+
const newSession = await ctx.context.internalAdapter.createSession(user.id, false, session.session).catch((e) => {
|
|
717
|
+
throw e;
|
|
718
|
+
});
|
|
719
|
+
await ctx.context.internalAdapter.deleteSession(session.session.token);
|
|
720
|
+
await setSessionCookie(ctx, {
|
|
721
|
+
session: newSession,
|
|
722
|
+
user: updatedUser
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
return valid(ctx);
|
|
726
|
+
})
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/plugins/two-factor/index.ts
|
|
733
|
+
const twoFactor = (options) => {
|
|
734
|
+
const opts = { twoFactorTable: "twoFactor" };
|
|
735
|
+
const backupCodeOptions = {
|
|
736
|
+
storeBackupCodes: "encrypted",
|
|
737
|
+
...options?.backupCodeOptions
|
|
738
|
+
};
|
|
739
|
+
const totp = totp2fa(options?.totpOptions);
|
|
740
|
+
const backupCode = backupCode2fa(backupCodeOptions);
|
|
741
|
+
const otp = otp2fa(options?.otpOptions);
|
|
742
|
+
return {
|
|
743
|
+
id: "two-factor",
|
|
744
|
+
endpoints: {
|
|
745
|
+
...totp.endpoints,
|
|
746
|
+
...otp.endpoints,
|
|
747
|
+
...backupCode.endpoints,
|
|
748
|
+
enableTwoFactor: createAuthEndpoint("/two-factor/enable", {
|
|
749
|
+
method: "POST",
|
|
750
|
+
body: z$1.object({
|
|
751
|
+
password: z$1.string().meta({ description: "User password" }),
|
|
752
|
+
issuer: z$1.string().meta({ description: "Custom issuer for the TOTP URI" }).optional()
|
|
753
|
+
}),
|
|
754
|
+
use: [sessionMiddleware],
|
|
755
|
+
metadata: { openapi: {
|
|
756
|
+
summary: "Enable two factor authentication",
|
|
757
|
+
description: "Use this endpoint to enable two factor authentication. This will generate a TOTP URI and backup codes. Once the user verifies the TOTP URI, the two factor authentication will be enabled.",
|
|
758
|
+
responses: { 200: {
|
|
759
|
+
description: "Successful response",
|
|
760
|
+
content: { "application/json": { schema: {
|
|
761
|
+
type: "object",
|
|
762
|
+
properties: {
|
|
763
|
+
totpURI: {
|
|
764
|
+
type: "string",
|
|
765
|
+
description: "TOTP URI"
|
|
766
|
+
},
|
|
767
|
+
backupCodes: {
|
|
768
|
+
type: "array",
|
|
769
|
+
items: { type: "string" },
|
|
770
|
+
description: "Backup codes"
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
} } }
|
|
774
|
+
} }
|
|
775
|
+
} }
|
|
776
|
+
}, async (ctx) => {
|
|
777
|
+
const user = ctx.context.session.user;
|
|
778
|
+
const { password, issuer } = ctx.body;
|
|
779
|
+
if (!await validatePassword(ctx, {
|
|
780
|
+
password,
|
|
781
|
+
userId: user.id
|
|
782
|
+
})) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.INVALID_PASSWORD });
|
|
783
|
+
const secret = generateRandomString(32);
|
|
784
|
+
const encryptedSecret = await symmetricEncrypt({
|
|
785
|
+
key: ctx.context.secret,
|
|
786
|
+
data: secret
|
|
787
|
+
});
|
|
788
|
+
const backupCodes = await generateBackupCodes(ctx.context.secret, backupCodeOptions);
|
|
789
|
+
if (options?.skipVerificationOnEnable) {
|
|
790
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: true });
|
|
791
|
+
/**
|
|
792
|
+
* Update the session cookie with the new user data
|
|
793
|
+
*/
|
|
794
|
+
await setSessionCookie(ctx, {
|
|
795
|
+
session: await ctx.context.internalAdapter.createSession(updatedUser.id, false, ctx.context.session.session),
|
|
796
|
+
user: updatedUser
|
|
797
|
+
});
|
|
798
|
+
await ctx.context.internalAdapter.deleteSession(ctx.context.session.session.token);
|
|
799
|
+
}
|
|
800
|
+
await ctx.context.adapter.deleteMany({
|
|
801
|
+
model: opts.twoFactorTable,
|
|
802
|
+
where: [{
|
|
803
|
+
field: "userId",
|
|
804
|
+
value: user.id
|
|
805
|
+
}]
|
|
806
|
+
});
|
|
807
|
+
await ctx.context.adapter.create({
|
|
808
|
+
model: opts.twoFactorTable,
|
|
809
|
+
data: {
|
|
810
|
+
secret: encryptedSecret,
|
|
811
|
+
backupCodes: backupCodes.encryptedBackupCodes,
|
|
812
|
+
userId: user.id
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
const totpURI = createOTP(secret, {
|
|
816
|
+
digits: options?.totpOptions?.digits || 6,
|
|
817
|
+
period: options?.totpOptions?.period
|
|
818
|
+
}).url(issuer || options?.issuer || ctx.context.appName, user.email);
|
|
819
|
+
return ctx.json({
|
|
820
|
+
totpURI,
|
|
821
|
+
backupCodes: backupCodes.backupCodes
|
|
822
|
+
});
|
|
823
|
+
}),
|
|
824
|
+
disableTwoFactor: createAuthEndpoint("/two-factor/disable", {
|
|
825
|
+
method: "POST",
|
|
826
|
+
body: z$1.object({ password: z$1.string().meta({ description: "User password" }) }),
|
|
827
|
+
use: [sessionMiddleware],
|
|
828
|
+
metadata: { openapi: {
|
|
829
|
+
summary: "Disable two factor authentication",
|
|
830
|
+
description: "Use this endpoint to disable two factor authentication.",
|
|
831
|
+
responses: { 200: {
|
|
832
|
+
description: "Successful response",
|
|
833
|
+
content: { "application/json": { schema: {
|
|
834
|
+
type: "object",
|
|
835
|
+
properties: { status: { type: "boolean" } }
|
|
836
|
+
} } }
|
|
837
|
+
} }
|
|
838
|
+
} }
|
|
839
|
+
}, async (ctx) => {
|
|
840
|
+
const user = ctx.context.session.user;
|
|
841
|
+
const { password } = ctx.body;
|
|
842
|
+
if (!await validatePassword(ctx, {
|
|
843
|
+
password,
|
|
844
|
+
userId: user.id
|
|
845
|
+
})) throw new APIError("BAD_REQUEST", { message: "Invalid password" });
|
|
846
|
+
const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: false });
|
|
847
|
+
await ctx.context.adapter.delete({
|
|
848
|
+
model: opts.twoFactorTable,
|
|
849
|
+
where: [{
|
|
850
|
+
field: "userId",
|
|
851
|
+
value: updatedUser.id
|
|
852
|
+
}]
|
|
853
|
+
});
|
|
854
|
+
/**
|
|
855
|
+
* Update the session cookie with the new user data
|
|
856
|
+
*/
|
|
857
|
+
await setSessionCookie(ctx, {
|
|
858
|
+
session: await ctx.context.internalAdapter.createSession(updatedUser.id, false, ctx.context.session.session),
|
|
859
|
+
user: updatedUser
|
|
860
|
+
});
|
|
861
|
+
await ctx.context.internalAdapter.deleteSession(ctx.context.session.session.token);
|
|
862
|
+
return ctx.json({ status: true });
|
|
863
|
+
})
|
|
864
|
+
},
|
|
865
|
+
options,
|
|
866
|
+
hooks: { after: [{
|
|
867
|
+
matcher(context) {
|
|
868
|
+
return context.path === "/sign-in/email" || context.path === "/sign-in/phone-number";
|
|
869
|
+
},
|
|
870
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
871
|
+
const data = ctx.context.newSession;
|
|
872
|
+
if (!data) return;
|
|
873
|
+
if (!data?.user.twoFactorEnabled) return;
|
|
874
|
+
const trustDeviceCookieName = ctx.context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME);
|
|
875
|
+
const trustDeviceCookie = await ctx.getSignedCookie(trustDeviceCookieName.name, ctx.context.secret);
|
|
876
|
+
if (trustDeviceCookie) {
|
|
877
|
+
const [token, sessionToken] = trustDeviceCookie.split("!");
|
|
878
|
+
if (token === await createHMAC("SHA-256", "base64urlnopad").sign(ctx.context.secret, `${data.user.id}!${sessionToken}`)) {
|
|
879
|
+
const newToken = await createHMAC("SHA-256", "base64urlnopad").sign(ctx.context.secret, `${data.user.id}!${sessionToken}`);
|
|
880
|
+
await ctx.setSignedCookie(trustDeviceCookieName.name, `${newToken}!${data.session.token}`, ctx.context.secret, trustDeviceCookieName.attributes);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* remove the session cookie. It's set by the sign in credential
|
|
886
|
+
*/
|
|
887
|
+
deleteSessionCookie(ctx, true);
|
|
888
|
+
await ctx.context.internalAdapter.deleteSession(data.session.token);
|
|
889
|
+
const maxAge = (options?.otpOptions?.period ?? 3) * 60;
|
|
890
|
+
const twoFactorCookie = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME, { maxAge });
|
|
891
|
+
const identifier = `2fa-${generateRandomString(20)}`;
|
|
892
|
+
await ctx.context.internalAdapter.createVerificationValue({
|
|
893
|
+
value: data.user.id,
|
|
894
|
+
identifier,
|
|
895
|
+
expiresAt: new Date(Date.now() + maxAge * 1e3)
|
|
896
|
+
});
|
|
897
|
+
await ctx.setSignedCookie(twoFactorCookie.name, identifier, ctx.context.secret, twoFactorCookie.attributes);
|
|
898
|
+
return ctx.json({ twoFactorRedirect: true });
|
|
899
|
+
})
|
|
900
|
+
}] },
|
|
901
|
+
schema: mergeSchema(schema, options?.schema),
|
|
902
|
+
rateLimit: [{
|
|
903
|
+
pathMatcher(path) {
|
|
904
|
+
return path.startsWith("/two-factor/");
|
|
905
|
+
},
|
|
906
|
+
window: 10,
|
|
907
|
+
max: 3
|
|
908
|
+
}],
|
|
909
|
+
$ERROR_CODES: TWO_FACTOR_ERROR_CODES
|
|
910
|
+
};
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
//#endregion
|
|
914
|
+
export { TWO_FACTOR_ERROR_CODES as n, twoFactor as t };
|