diff --git a/package.json b/package.json index 89208f0..d4f8c4f 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@sveltejs/kit": "^2.5.5", "@sveltejs/vite-plugin-svelte": "^3.0.2", "@types/cookie": "^0.6.0", - "@types/node": "^20.12.4", + "@types/node": "^20.12.5", "@types/pg": "^8.11.4", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", @@ -102,7 +102,7 @@ "loader": "^2.1.1", "lucia": "3.1.1", "lucide-svelte": "^0.358.0", - "open-props": "^1.7.0", + "open-props": "^1.7.2", "oslo": "^1.2.0", "pg": "^8.11.5", "postgres": "^3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cb8b8e..162831f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,8 +87,8 @@ dependencies: specifier: ^0.358.0 version: 0.358.0(svelte@4.2.12) open-props: - specifier: ^1.7.0 - version: 1.7.0 + specifier: ^1.7.2 + version: 1.7.2 oslo: specifier: ^1.2.0 version: 1.2.0 @@ -152,8 +152,8 @@ devDependencies: specifier: ^0.6.0 version: 0.6.0 '@types/node': - specifier: ^20.12.4 - version: 20.12.4 + specifier: ^20.12.5 + version: 20.12.5 '@types/pg': specifier: ^8.11.4 version: 8.11.4 @@ -246,7 +246,7 @@ devDependencies: version: 3.4.3(ts-node@10.9.2) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.12.4)(typescript@5.4.4) + version: 10.9.2(@types/node@20.12.5)(typescript@5.4.4) tslib: specifier: ^2.6.1 version: 2.6.2 @@ -258,10 +258,10 @@ devDependencies: version: 5.4.4 vite: specifier: ^5.2.8 - version: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + version: 5.2.8(@types/node@20.12.5)(sass@1.74.1) vitest: specifier: ^1.4.0 - version: 1.4.0(@types/node@20.12.4)(sass@1.74.1) + version: 1.4.0(@types/node@20.12.5)(sass@1.74.1) zod: specifier: ^3.22.4 version: 3.22.4 @@ -3286,7 +3286,7 @@ packages: sirv: 2.0.4 svelte: 4.2.12 tiny-glob: 0.2.9 - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) /@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.8): resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==} @@ -3299,7 +3299,7 @@ packages: '@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.2.8) debug: 4.3.4 svelte: 4.2.12 - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) transitivePeerDependencies: - supports-color @@ -3317,7 +3317,7 @@ packages: magic-string: 0.30.5 svelte: 4.2.12 svelte-hmr: 0.15.3(svelte@4.2.12) - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) vitefu: 0.2.5(vite@5.2.8) transitivePeerDependencies: - supports-color @@ -3360,22 +3360,22 @@ packages: /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - /@types/node@20.12.4: - resolution: {integrity: sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==} + /@types/node@20.12.5: + resolution: {integrity: sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==} dependencies: undici-types: 5.26.5 /@types/pg@8.11.4: resolution: {integrity: sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==} dependencies: - '@types/node': 20.12.4 + '@types/node': 20.12.5 pg-protocol: 1.6.0 pg-types: 4.0.2 /@types/pg@8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: - '@types/node': 20.12.4 + '@types/node': 20.12.5 pg-protocol: 1.6.0 pg-types: 2.2.0 dev: false @@ -5851,8 +5851,8 @@ packages: mimic-fn: 4.0.0 dev: true - /open-props@1.7.0: - resolution: {integrity: sha512-exvA+8HSxD5qihtBnaDQ1uSKrZV/hfM4/K6UCY7LMzwiMKwejshuNPVpM7exoe74wluOq1NdWYmoSfFxGW9iyw==} + /open-props@1.7.2: + resolution: {integrity: sha512-RheKypVzZBCSZ6c5iJaFWG0OBqdtql3eRFXRYrSNLh6vGzU8NSAHuq9iJPj++DrpPGs1pqlRa2BelwwBHjX3Xg==} dev: false /optionator@0.9.3: @@ -6334,7 +6334,7 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.38 - ts-node: 10.9.2(@types/node@20.12.4)(typescript@5.4.4) + ts-node: 10.9.2(@types/node@20.12.5)(typescript@5.4.4) yaml: 1.10.2 dev: true @@ -6352,7 +6352,7 @@ packages: dependencies: lilconfig: 3.0.0 postcss: 8.4.38 - ts-node: 10.9.2(@types/node@20.12.4)(typescript@5.4.4) + ts-node: 10.9.2(@types/node@20.12.5)(typescript@5.4.4) yaml: 2.3.4 /postcss-load-config@5.0.3(postcss@8.4.38): @@ -7365,23 +7365,6 @@ packages: peerDependencies: '@sveltejs/kit': 1.x || 2.x svelte: 3.x || 4.x || >=5.0.0-next.51 - peerDependenciesMeta: - '@sinclair/typebox': - optional: true - '@vinejs/vine': - optional: true - arktype: - optional: true - joi: - optional: true - superstruct: - optional: true - valibot: - optional: true - yup: - optional: true - zod: - optional: true dependencies: '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@4.2.12)(vite@5.2.8) devalue: 4.3.2 @@ -7574,7 +7557,7 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - /ts-node@10.9.2(@types/node@20.12.4)(typescript@5.4.4): + /ts-node@10.9.2(@types/node@20.12.5)(typescript@5.4.4): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -7593,7 +7576,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.12.4 + '@types/node': 20.12.5 acorn: 8.11.2 acorn-walk: 8.3.0 arg: 4.1.3 @@ -7742,7 +7725,7 @@ packages: - rollup dev: true - /vite-node@1.4.0(@types/node@20.12.4)(sass@1.74.1): + /vite-node@1.4.0(@types/node@20.12.5)(sass@1.74.1): resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -7751,7 +7734,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) transitivePeerDependencies: - '@types/node' - less @@ -7763,7 +7746,7 @@ packages: - terser dev: true - /vite@5.2.8(@types/node@20.12.4)(sass@1.74.1): + /vite@5.2.8(@types/node@20.12.5)(sass@1.74.1): resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -7791,7 +7774,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.12.4 + '@types/node': 20.12.5 esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.13.0 @@ -7807,9 +7790,9 @@ packages: vite: optional: true dependencies: - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) - /vitest@1.4.0(@types/node@20.12.4)(sass@1.74.1): + /vitest@1.4.0(@types/node@20.12.5)(sass@1.74.1): resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -7834,7 +7817,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.12.4 + '@types/node': 20.12.5 '@vitest/expect': 1.4.0 '@vitest/runner': 1.4.0 '@vitest/snapshot': 1.4.0 @@ -7852,8 +7835,8 @@ packages: strip-literal: 2.0.0 tinybench: 2.6.0 tinypool: 0.8.2 - vite: 5.2.8(@types/node@20.12.4)(sass@1.74.1) - vite-node: 1.4.0(@types/node@20.12.4)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.5)(sass@1.74.1) + vite-node: 1.4.0(@types/node@20.12.5)(sass@1.74.1) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/routes/(app)/(protected)/profile/+page.server.ts b/src/routes/(app)/(protected)/profile/+page.server.ts index efab70d..54e02d1 100644 --- a/src/routes/(app)/(protected)/profile/+page.server.ts +++ b/src/routes/(app)/(protected)/profile/+page.server.ts @@ -37,7 +37,7 @@ export const load: PageServerLoad = async (event) => { return { profileForm, emailForm, - hasSetupTwoFactor: !!dbUser?.two_factor_secret, + hasSetupTwoFactor: !!dbUser?.two_factor_enabled, }; }; diff --git a/src/routes/(app)/(protected)/profile/security/two-factor/+page.server.ts b/src/routes/(app)/(protected)/profile/security/two-factor/+page.server.ts index eaf024a..cbbc52b 100644 --- a/src/routes/(app)/(protected)/profile/security/two-factor/+page.server.ts +++ b/src/routes/(app)/(protected)/profile/security/two-factor/+page.server.ts @@ -1,19 +1,18 @@ import { type Actions, fail } from '@sveltejs/kit'; import { eq } from 'drizzle-orm'; -import { createId as cuid2 } from '@paralleldrive/cuid2'; import { encodeHex, decodeHex } from 'oslo/encoding'; import { Argon2id } from 'oslo/password'; import { createTOTPKeyURI, TOTPController } from 'oslo/otp'; import { HMAC } from 'oslo/crypto'; import QRCode from 'qrcode'; import { zod } from 'sveltekit-superforms/adapters'; -import { message, setError, superValidate } from 'sveltekit-superforms/server'; +import { setError, superValidate } from 'sveltekit-superforms/server'; import { redirect } from 'sveltekit-flash-message/server'; import type { PageServerLoad } from '../../$types'; import { addTwoFactorSchema } from '$lib/validations/account'; import { notSignedInMessage } from '$lib/flashMessages'; import db from '$lib/drizzle'; -import { recovery_codes, users } from '../../../../../../schema'; +import { users } from '../../../../../../schema'; export const load: PageServerLoad = async (event) => { const form = await superValidate(event, zod(addTwoFactorSchema)); @@ -23,41 +22,6 @@ export const load: PageServerLoad = async (event) => { redirect(302, '/login', notSignedInMessage, event); } - const dbUser = await db.query.users.findFirst({ - where: eq(users.id, user.id), - }); - - if (dbUser?.two_factor_enabled) { - const recoveryCodes = await db.query.recovery_codes.findMany({ - where: eq(recovery_codes.userId, user.id), - }); - - if (recoveryCodes.length === 0) { - const recoveryCodes = generateRecoveryCodes(); - if (recoveryCodes) { - for (const code of recoveryCodes) { - await db.insert(recovery_codes).values({ - userId: user.id, - code: await new Argon2id().hash(code), - }); - } - } - return { - form, - twoFactorEnabled: true, - recoveryCodes, - totpUri: '', - qrCode: '', - }; - } - - const message = { - type: 'info', - message: 'Two-Factor Authentication is already enabled', - } as const; - throw redirect('/profile', message, event); - } - const twoFactorSecret = await new HMAC('SHA-1').generateKey(); await db .update(users) @@ -71,8 +35,6 @@ export const load: PageServerLoad = async (event) => { const accountName = user.email || user.username; // pass the website's name and the user identifier (e.g. email, username) const totpUri = createTOTPKeyURI(issuer, accountName, twoFactorSecret); - const qrCode = await QRCode.toDataURL(totpUri); - console.log('QR Code: ', qrCode); form.data = { current_password: '', @@ -83,7 +45,7 @@ export const load: PageServerLoad = async (event) => { twoFactorEnabled: false, recoveryCodes: [], totpUri, - qrCode, + qrCode: await QRCode.toDataURL(totpUri), }; }; @@ -154,15 +116,6 @@ export const actions: Actions = { await db.update(users).set({ two_factor_enabled: true }).where(eq(users.id, user.id)); - form.data.current_password = ''; - form.data.two_factor_code = ''; - return { - form, - twoFactorEnabled: true, - }; + redirect(302, '/profile/security/two-factor/recovery-codes'); }, }; - -function generateRecoveryCodes() { - return Array.from({ length: 5 }, () => cuid2()); -} diff --git a/src/routes/(app)/(protected)/profile/security/two-factor/+page.svelte b/src/routes/(app)/(protected)/profile/security/two-factor/+page.svelte index 3d982ac..d7c5d72 100644 --- a/src/routes/(app)/(protected)/profile/security/two-factor/+page.svelte +++ b/src/routes/(app)/(protected)/profile/security/two-factor/+page.svelte @@ -18,6 +18,8 @@ multipleSubmits: 'prevent', }); + console.log('Two Factor: ', twoFactorEnabled, recoveryCodes); + const { form: formData, enhance } = form; @@ -25,16 +27,10 @@ {#if twoFactorEnabled}
{code}
- {/each} - {/if} {:else}{code}
+ {/each} + {/if} +{:else} +If you wish to generate new codes, please disable and then re-enable two-factor authentication.
+{/if} \ No newline at end of file