diff --git a/package.json b/package.json index e4997a2..bddfaf3 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ }, "type": "module", "dependencies": { - "@fontsource/fira-mono": "^5.0.14", + "@fontsource/fira-mono": "^5.0.15", "@hono/swagger-ui": "^0.4.1", "@hono/zod-openapi": "^0.15.3", "@hono/zod-validator": "^0.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c00d4b2..94e8b55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@fontsource/fira-mono': - specifier: ^5.0.14 - version: 5.0.14 + specifier: ^5.0.15 + version: 5.0.15 '@hono/swagger-ui': specifier: ^0.4.1 version: 0.4.1(hono@4.5.11) @@ -1245,8 +1245,8 @@ packages: '@floating-ui/utils@0.2.7': resolution: {integrity: sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==} - '@fontsource/fira-mono@5.0.14': - resolution: {integrity: sha512-4IKa+cuHipk/vr2frgZh4pyR2XcoQk/j3zmMlo8uuAGUB3IPLpQlgN6Qm5d3RfRZ7dXGlTn/PWiAJeU8bkmD4w==} + '@fontsource/fira-mono@5.0.15': + resolution: {integrity: sha512-wc3TpF2GBbtFDKNbb444BrC3mEKuoPLITSYCKweNIrqBvAalIfJGloY/MVrmSGaMNgaAKUpdgy4eAWPLkUVzaA==} '@gcornut/valibot-json-schema@0.31.0': resolution: {integrity: sha512-3xGptCurm23e7nuPQkdrE5rEs1FeTPHhAUsBuwwqG4/YeZLwJOoYZv+fmsppUEfo5y9lzUwNQrNqLS/q7HMc7g==} @@ -5489,7 +5489,7 @@ snapshots: '@floating-ui/utils@0.2.7': {} - '@fontsource/fira-mono@5.0.14': {} + '@fontsource/fira-mono@5.0.15': {} '@gcornut/valibot-json-schema@0.31.0': dependencies: diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte index f614316..4b3a06b 100644 --- a/src/lib/components/Header.svelte +++ b/src/lib/components/Header.svelte @@ -4,15 +4,10 @@ import { invalidateAll } from '$app/navigation' import Logo from '$components/logo.svelte' import * as Avatar from '$components/ui/avatar' import * as DropdownMenu from '$components/ui/dropdown-menu' -import type { Users } from '$db/schema' -import { ListChecks, ListTodo, LogOut, User } from 'lucide-svelte' +import { ListChecks, ListTodo, LogOut, Settings } from 'lucide-svelte' import toast from 'svelte-french-toast' -type HeaderProps = { - user: Users | null -} - -let { user = null }: HeaderProps = $props() +let { user = null } = $props() let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)') @@ -28,39 +23,48 @@ let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)') + + +{#snippet userDropdown()} + + + + + {avatar} + + + + + + Account + + + + + Settings + + + + + + Collections + + + + + + Wishlists + + + + { return async ({ result }) => { console.log(result); if (result.type === 'success' || result.type === 'redirect') { @@ -76,26 +80,21 @@ let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)') await applyAction(result); }; }} - action="/logout" - method="POST" - > - - - - - - - {:else} - Login - Sign Up - {/if} - - + action="/logout" + method="POST" + > + + + + + + +{/snippet} \ No newline at end of file diff --git a/src/lib/components/ui/input/input.svelte b/src/lib/components/ui/input/input.svelte index cab1457..477d325 100644 --- a/src/lib/components/ui/input/input.svelte +++ b/src/lib/components/ui/input/input.svelte @@ -1,7 +1,7 @@
diff --git a/src/lib/server/api/common/config.ts b/src/lib/server/api/common/config.ts index 518c73f..0daf6d4 100644 --- a/src/lib/server/api/common/config.ts +++ b/src/lib/server/api/common/config.ts @@ -34,5 +34,3 @@ export const config: Config = { max: env.DB_MIGRATING || env.DB_SEEDING ? 1 : undefined, }, } - -console.log('config', config) diff --git a/src/lib/server/api/common/exceptions.ts b/src/lib/server/api/common/exceptions.ts index fee54c4..cbda928 100644 --- a/src/lib/server/api/common/exceptions.ts +++ b/src/lib/server/api/common/exceptions.ts @@ -1,26 +1,26 @@ -import { StatusCodes } from '$lib/constants/status-codes'; -import { HTTPException } from 'hono/http-exception'; +import { StatusCodes } from '$lib/constants/status-codes' +import { HTTPException } from 'hono/http-exception' -export function TooManyRequests(message: string = 'Too many requests') { - return new HTTPException(StatusCodes.TOO_MANY_REQUESTS, { message }); +export function TooManyRequests(message = 'Too many requests') { + return new HTTPException(StatusCodes.TOO_MANY_REQUESTS, { message }) } -export function Forbidden(message: string = 'Forbidden') { - return new HTTPException(StatusCodes.FORBIDDEN, { message }); +export function Forbidden(message = 'Forbidden') { + return new HTTPException(StatusCodes.FORBIDDEN, { message }) } -export function Unauthorized(message: string = 'Unauthorized') { - return new HTTPException(StatusCodes.UNAUTHORIZED, { message }); +export function Unauthorized(message = 'Unauthorized') { + return new HTTPException(StatusCodes.UNAUTHORIZED, { message }) } -export function NotFound(message: string = 'Not Found') { - return new HTTPException(StatusCodes.NOT_FOUND, { message }); +export function NotFound(message = 'Not Found') { + return new HTTPException(StatusCodes.NOT_FOUND, { message }) } -export function BadRequest(message: string = 'Bad Request') { - return new HTTPException(StatusCodes.BAD_REQUEST, { message }); +export function BadRequest(message = 'Bad Request') { + return new HTTPException(StatusCodes.BAD_REQUEST, { message }) } -export function InternalError(message: string = 'Internal Error') { - return new HTTPException(StatusCodes.INTERNAL_SERVER_ERROR, { message }); +export function InternalError(message = 'Internal Error') { + return new HTTPException(StatusCodes.INTERNAL_SERVER_ERROR, { message }) } diff --git a/src/lib/server/api/controllers/collection.controller.ts b/src/lib/server/api/controllers/collection.controller.ts index 56a53aa..86dfcc5 100644 --- a/src/lib/server/api/controllers/collection.controller.ts +++ b/src/lib/server/api/controllers/collection.controller.ts @@ -2,7 +2,7 @@ import 'reflect-metadata' import { Controller } from '$lib/server/api/common/types/controller' import { CollectionsService } from '$lib/server/api/services/collections.service' import { inject, injectable } from 'tsyringe' -import { requireAuth } from '../middleware/auth.middleware' +import { requireAuth } from '../middleware/require-auth.middleware' @injectable() export class CollectionController extends Controller { diff --git a/src/lib/server/api/controllers/iam.controller.ts b/src/lib/server/api/controllers/iam.controller.ts index 708b7ef..0d2010c 100644 --- a/src/lib/server/api/controllers/iam.controller.ts +++ b/src/lib/server/api/controllers/iam.controller.ts @@ -1,20 +1,23 @@ import { StatusCodes } from '$lib/constants/status-codes' import { Controller } from '$lib/server/api/common/types/controller' +import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto' import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto' import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto' import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto' import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware' import { IamService } from '$lib/server/api/services/iam.service' +import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service' import { LuciaService } from '$lib/server/api/services/lucia.service' import { zValidator } from '@hono/zod-validator' import { setCookie } from 'hono/cookie' import { inject, injectable } from 'tsyringe' -import { requireAuth } from '../middleware/auth.middleware' +import { requireAuth } from '../middleware/require-auth.middleware' @injectable() export class IamController extends Controller { constructor( @inject(IamService) private readonly iamService: IamService, + @inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, @inject(LuciaService) private luciaService: LuciaService, ) { super() @@ -45,6 +48,32 @@ export class IamController extends Controller { } return c.json({}, StatusCodes.OK) }) + .put('/update/password', requireAuth, zValidator('json', changePasswordDto), limiter({ limit: 10, minutes: 60 }), async (c) => { + const user = c.var.user + const { password, confirm_password } = c.req.valid('json') + if (password !== confirm_password) { + return c.json('Passwords do not match', StatusCodes.BAD_REQUEST) + } + try { + await this.iamService.updatePassword(user.id, { password, confirm_password }) + await this.luciaService.lucia.invalidateUserSessions(user.id) + await this.loginRequestService.createUserSession(user.id, c.req, undefined) + const sessionCookie = this.luciaService.lucia.createBlankSessionCookie() + setCookie(c, sessionCookie.name, sessionCookie.value, { + path: sessionCookie.attributes.path, + maxAge: sessionCookie.attributes.maxAge, + domain: sessionCookie.attributes.domain, + sameSite: sessionCookie.attributes.sameSite as any, + secure: sessionCookie.attributes.secure, + httpOnly: sessionCookie.attributes.httpOnly, + expires: sessionCookie.attributes.expires, + }) + return c.json({ status: 'success' }) + } catch (error) { + console.error('Error updating password', error) + return c.json('Error updating password', StatusCodes.BAD_REQUEST) + } + }) .post('/update/email', requireAuth, zValidator('json', updateEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => { const user = c.var.user const { email } = c.req.valid('json') diff --git a/src/lib/server/api/controllers/mfa.controller.ts b/src/lib/server/api/controllers/mfa.controller.ts index f70aa04..b08b8c1 100644 --- a/src/lib/server/api/controllers/mfa.controller.ts +++ b/src/lib/server/api/controllers/mfa.controller.ts @@ -8,7 +8,7 @@ import { UsersService } from '$lib/server/api/services/users.service' import { zValidator } from '@hono/zod-validator' import { inject, injectable } from 'tsyringe' import { CredentialsType } from '../databases/tables' -import { requireAuth } from '../middleware/auth.middleware' +import { requireAuth } from '../middleware/require-auth.middleware' @injectable() export class MfaController extends Controller { diff --git a/src/lib/server/api/controllers/user.controller.ts b/src/lib/server/api/controllers/user.controller.ts index e7c5deb..c05cd2e 100644 --- a/src/lib/server/api/controllers/user.controller.ts +++ b/src/lib/server/api/controllers/user.controller.ts @@ -2,7 +2,7 @@ import 'reflect-metadata' import { Controller } from '$lib/server/api/common/types/controller' import { UsersService } from '$lib/server/api/services/users.service' import { inject, injectable } from 'tsyringe' -import { requireAuth } from '../middleware/auth.middleware' +import { requireAuth } from '../middleware/require-auth.middleware' @injectable() export class UserController extends Controller { diff --git a/src/lib/server/api/controllers/wishlist.controller.ts b/src/lib/server/api/controllers/wishlist.controller.ts index 44a4a11..1a8da1c 100644 --- a/src/lib/server/api/controllers/wishlist.controller.ts +++ b/src/lib/server/api/controllers/wishlist.controller.ts @@ -2,7 +2,7 @@ import 'reflect-metadata' import { Controller } from '$lib/server/api/common/types/controller' import { WishlistsService } from '$lib/server/api/services/wishlists.service' import { inject, injectable } from 'tsyringe' -import { requireAuth } from '../middleware/auth.middleware' +import { requireAuth } from '../middleware/require-auth.middleware' @injectable() export class WishlistController extends Controller { diff --git a/src/lib/server/api/dtos/change-password.dto.ts b/src/lib/server/api/dtos/change-password.dto.ts new file mode 100644 index 0000000..b624722 --- /dev/null +++ b/src/lib/server/api/dtos/change-password.dto.ts @@ -0,0 +1,17 @@ +import { refinePasswords } from '$lib/validations/account' +import { z } from 'zod' + +export const changePasswordDto = z + .object({ + password: z.string({ required_error: 'Password is required' }).trim(), + confirm_password: z + .string({ required_error: 'Confirm Password is required' }) + .trim() + .min(8, { message: 'Must be at least 8 characters' }) + .max(128, { message: 'Must be less than 128 characters' }), + }) + .superRefine(({ confirm_password, password }, ctx) => { + return refinePasswords(confirm_password, password, ctx) + }) + +export type ChangePasswordDto = z.infer diff --git a/src/lib/server/api/dtos/update-profile.dto.ts b/src/lib/server/api/dtos/update-profile.dto.ts index 9ea0c6f..1ce379b 100644 --- a/src/lib/server/api/dtos/update-profile.dto.ts +++ b/src/lib/server/api/dtos/update-profile.dto.ts @@ -1,23 +1,14 @@ -import { z } from "zod"; +import { z } from 'zod' export const updateProfileDto = z.object({ firstName: z .string() .trim() - .min(3, {message: 'Must be at least 3 characters'}) - .max(50, {message: 'Must be less than 50 characters'}) + .min(3, { message: 'Must be at least 3 characters' }) + .max(50, { message: 'Must be less than 50 characters' }) .optional(), - lastName: z - .string() - .trim() - .min(3, {message: 'Must be at least 3 characters'}) - .max(50, {message: 'Must be less than 50 characters'}) - .optional(), - username: z - .string() - .trim() - .min(3, {message: 'Must be at least 3 characters'}) - .max(50, {message: 'Must be less than 50 characters'}) -}); + lastName: z.string().trim().min(3, { message: 'Must be at least 3 characters' }).max(50, { message: 'Must be less than 50 characters' }).optional(), + username: z.string().trim().min(3, { message: 'Must be at least 3 characters' }).max(50, { message: 'Must be less than 50 characters' }), +}) -export type UpdateProfileDto = z.infer; +export type UpdateProfileDto = z.infer diff --git a/src/lib/server/api/jobs/auth-cleanup.job.ts b/src/lib/server/api/jobs/auth-cleanup.job.ts index fb2ec6c..efaf1c1 100644 --- a/src/lib/server/api/jobs/auth-cleanup.job.ts +++ b/src/lib/server/api/jobs/auth-cleanup.job.ts @@ -10,11 +10,11 @@ export class AuthCleanupJobs { this.queue = this.jobsService.createQueue('test') /* ---------------------------- Register Workers ---------------------------- */ - this.worker().then((r) => console.log('auth-cleanup job worker started')) + this.worker().then(() => console.log('auth-cleanup job worker started')) } async deleteStaleEmailVerificationRequests() { - await this.queue.add('delete_stale_email_verifiactions', null, { + await this.queue.add('delete_stale_email_verifications', null, { repeat: { pattern: '0 0 * * 0', // Runs once a week at midnight on Sunday }, @@ -31,7 +31,7 @@ export class AuthCleanupJobs { private async worker() { return this.jobsService.createWorker(this.queue.name, async (job) => { - if (job.name === 'delete_stale_email_verifiactions') { + if (job.name === 'delete_stale_email_verifications') { // delete stale email verifications } if (job.name === 'delete_stale_login_requests') { diff --git a/src/lib/server/api/middleware/auth.middleware.ts b/src/lib/server/api/middleware/auth.middleware.ts index 3cc3766..b88418b 100644 --- a/src/lib/server/api/middleware/auth.middleware.ts +++ b/src/lib/server/api/middleware/auth.middleware.ts @@ -1,10 +1,8 @@ import { LuciaService } from '$lib/server/api/services/lucia.service' import type { MiddlewareHandler } from 'hono' import { createMiddleware } from 'hono/factory' -import type { Session, User } from 'lucia' import { verifyRequestOrigin } from 'oslo/request' import { container } from 'tsyringe' -import { Unauthorized } from '../common/exceptions' import type { HonoTypes } from '../common/types/hono' // resolve dependencies from the container @@ -41,14 +39,3 @@ export const validateAuthSession: MiddlewareHandler = createMiddlewar c.set('user', user) return next() }) - -export const requireAuth: MiddlewareHandler<{ - Variables: { - session: Session - user: User - } -}> = createMiddleware(async (c, next) => { - const user = c.var.user - if (!user) throw Unauthorized('You must be logged in to access this resource') - return next() -}) diff --git a/src/lib/server/api/services/iam.service.ts b/src/lib/server/api/services/iam.service.ts index 0c3bc6e..a70e8c3 100644 --- a/src/lib/server/api/services/iam.service.ts +++ b/src/lib/server/api/services/iam.service.ts @@ -1,3 +1,5 @@ +import { CredentialsType } from '$lib/server/api/databases/tables' +import type { ChangePasswordDto } from '$lib/server/api/dtos/change-password.dto' import type { UpdateEmailDto } from '$lib/server/api/dtos/update-email.dto' import type { UpdateProfileDto } from '$lib/server/api/dtos/update-profile.dto' import type { VerifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto' @@ -68,6 +70,11 @@ export class IamService { }) } + async updatePassword(userId: string, data: ChangePasswordDto) { + const { password } = data + await this.usersService.updatePassword(userId, password) + } + async verifyPassword(userId: string, data: VerifyPasswordDto) { const user = await this.usersService.findOneById(userId) if (!user) { diff --git a/src/lib/server/api/services/users.service.ts b/src/lib/server/api/services/users.service.ts index 89e6eea..f067809 100644 --- a/src/lib/server/api/services/users.service.ts +++ b/src/lib/server/api/services/users.service.ts @@ -69,6 +69,22 @@ export class UsersService { return this.usersRepository.findOneById(id) } + async updatePassword(userId: string, password: string) { + const hashedPassword = await this.tokenService.createHashedToken(password) + const currentCredentials = await this.credentialsRepository.findPasswordCredentialsByUserId(userId) + if (!currentCredentials) { + await this.credentialsRepository.create({ + user_id: userId, + type: CredentialsType.PASSWORD, + secret_data: hashedPassword, + }) + } else { + await this.credentialsRepository.update(currentCredentials.id, { + secret_data: hashedPassword, + }) + } + } + async verifyPassword(userId: string, data: { password: string }) { const user = await this.usersRepository.findOneById(userId) if (!user) { diff --git a/src/routes/(app)/(protected)/settings/+layout.svelte b/src/routes/(app)/(protected)/settings/+layout.svelte index c4620d3..81483b4 100644 --- a/src/routes/(app)/(protected)/settings/+layout.svelte +++ b/src/routes/(app)/(protected)/settings/+layout.svelte @@ -1,5 +1,5 @@ - - {@render children()} - \ No newline at end of file +
+ +
+ {@render children()} +
+
+ + diff --git a/src/routes/(app)/(protected)/settings/security/change/password/+page.server.ts b/src/routes/(app)/(protected)/settings/security/change/password/+page.server.ts index e708aa5..1c4e775 100644 --- a/src/routes/(app)/(protected)/settings/security/change/password/+page.server.ts +++ b/src/routes/(app)/(protected)/settings/security/change/password/+page.server.ts @@ -48,33 +48,11 @@ export const actions: Actions = { }) } - console.log('updating profile') - if (!event.locals.user) { - redirect(302, '/login', notSignedInMessage, event) - } - - if (!event.locals.session) { - return fail(401) - } - - const dbUser = await db.query.usersTable.findFirst({ - where: eq(usersTable.id, authedUser.id), - }) - - // if (!dbUser?.hashed_password) { - // form.data.password = ''; - // form.data.confirm_password = ''; - // form.data.current_password = ''; - // return setError( - // form, - // 'Error occurred. Please try again or contact support if you need further help.', - // ); - // } - - const currentPasswordVerified = await new Argon2id().verify( - // dbUser.hashed_password, - form.data.current_password, - ) + const currentPasswordVerified = await locals.api.me.verify.password + .$post({ + json: { password: form.data.current_password }, + }) + .then(locals.parseApiResponse) if (!currentPasswordVerified) { return setError(form, 'current_password', 'Your password is incorrect') @@ -85,16 +63,9 @@ export const actions: Actions = { if (form.data.password !== form.data.confirm_password) { return setError(form, 'Password and confirm password do not match') } - const hashedPassword = await new Argon2id().hash(form.data.password) - await lucia.invalidateUserSessions(authedUser.id) - // await db - // .update(usersTable) - // .set({ hashed_password: hashedPassword }) - // .where(eq(usersTable.id, user.id)); - await lucia.createSession(user.id, { - country: event.locals.session?.ipCountry ?? 'unknown', - }) - sessionCookie = lucia.createBlankSessionCookie() + await locals.api.me.change.password.$put({ + json: { password: form.data.password, confirm_password: form.data.confirm_password }, + }).then(locals.parseApiResponse) } catch (e) { console.error(e) form.data.password = '' diff --git a/src/routes/(app)/(protected)/settings/security/change/password/+page.svelte b/src/routes/(app)/(protected)/settings/security/change/password/+page.svelte index 302379a..1a46b42 100644 --- a/src/routes/(app)/(protected)/settings/security/change/password/+page.svelte +++ b/src/routes/(app)/(protected)/settings/security/change/password/+page.svelte @@ -2,7 +2,8 @@ import * as Alert from '$components/ui/alert' import * as Form from '$components/ui/form' import { Input } from '$components/ui/input' -import { AlertTriangle } from 'lucide-svelte' +import { Toggle } from '$components/ui/toggle' +import { AlertTriangle, EyeIcon, EyeOff } from 'lucide-svelte' import { zodClient } from 'sveltekit-superforms/adapters' import { superForm } from 'sveltekit-superforms/client' import { changeUserPasswordSchema } from './schemas' @@ -16,13 +17,17 @@ const form = superForm(data.form, { multipleSubmits: 'prevent', }) +let hiddenCurrentPassword = $state(true) +let hiddenPassword = $state(true) +let hiddenConfirmPassword = $state(true) + const { form: formData, enhance } = form

Change Password


- + Heads up! @@ -32,21 +37,30 @@ const { form: formData, enhance } = form Current Password - + + + hiddenCurrentPassword = !hiddenCurrentPassword}>{#if hiddenCurrentPassword}{:else}{/if} + New Password - + + + hiddenPassword = !hiddenPassword}>{#if hiddenPassword}{:else}{/if} + Confirm New Password - + + + hiddenConfirmPassword = !hiddenConfirmPassword}>{#if hiddenConfirmPassword}{:else}{/if} + diff --git a/src/routes/(app)/(protected)/settings/security/change/password/schemas.ts b/src/routes/(app)/(protected)/settings/security/change/password/schemas.ts index ffb4ed8..a6f3adb 100644 --- a/src/routes/(app)/(protected)/settings/security/change/password/schemas.ts +++ b/src/routes/(app)/(protected)/settings/security/change/password/schemas.ts @@ -13,8 +13,8 @@ export const changeUserPasswordSchema = z export type ChangeUserPasswordSchema = typeof changeUserPasswordSchema const refinePasswords = async (confirm_password: string, password: string, ctx: z.RefinementCtx) => { - comparePasswords(confirm_password, password, ctx) - checkPasswordStrength(password, ctx) + await comparePasswords(confirm_password, password, ctx) + await checkPasswordStrength(password, ctx) } const comparePasswords = async (confirm_password: string, password: string, ctx: z.RefinementCtx) => { diff --git a/src/routes/(app)/(protected)/settings/security/mfa/+page.server.ts b/src/routes/(app)/(protected)/settings/security/mfa/+page.server.ts index e87d84b..d16dcd4 100644 --- a/src/routes/(app)/(protected)/settings/security/mfa/+page.server.ts +++ b/src/routes/(app)/(protected)/settings/security/mfa/+page.server.ts @@ -1,14 +1,6 @@ import { notSignedInMessage } from '$lib/flashMessages' -import env from '$lib/server/api/common/env' -import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/account' -import { type Actions, fail } from '@sveltejs/kit' -import kebabCase from 'just-kebab-case' -import { base32, decodeHex } from 'oslo/encoding' -import { createTOTPKeyURI } from 'oslo/otp' -import QRCode from 'qrcode' +import type { Actions } from '@sveltejs/kit' import { redirect } from 'sveltekit-flash-message/server' -import { zod } from 'sveltekit-superforms/adapters' -import { setError, superValidate } from 'sveltekit-superforms/server' import type { PageServerLoad } from '../../$types' export const load: PageServerLoad = async (event) => { @@ -19,7 +11,14 @@ export const load: PageServerLoad = async (event) => { throw redirect(302, '/login', notSignedInMessage, event) } - return {} + const { data: totpData, error: totpDataError } = await locals.api.mfa.totp.$get().then(locals.parseApiResponse) + + const totpEnabled = !!totpData + + return { + totpEnabled, + hardwareTokenEnabled: false, + } } export const actions: Actions = {} diff --git a/src/routes/(app)/(protected)/settings/security/mfa/+page.svelte b/src/routes/(app)/(protected)/settings/security/mfa/+page.svelte index 0ed96a6..94b3304 100644 --- a/src/routes/(app)/(protected)/settings/security/mfa/+page.svelte +++ b/src/routes/(app)/(protected)/settings/security/mfa/+page.svelte @@ -5,8 +5,8 @@ import * as Card from '$lib/components/ui/card' const { data } = $props() -const totpEnabled = true -const hardwareTokenEnabled = true +const totpEnabled = data.totpEnabled +const hardwareTokenEnabled = data.hardwareTokenEnabled

Two-factor authentication

@@ -19,7 +19,7 @@ const hardwareTokenEnabled = true
-

Authenticator app {#if hardwareTokenEnabled}Configured{/if}

+

Authenticator app {#if totpEnabled}Configured{/if}

Use an authenticator app or browser extension to get two-factor authentication codes when prompted.

diff --git a/src/routes/(app)/+layout.server.ts b/src/routes/(app)/+layout.server.ts index aa43781..493b195 100644 --- a/src/routes/(app)/+layout.server.ts +++ b/src/routes/(app)/+layout.server.ts @@ -1,24 +1,12 @@ -import { loadFlash } from 'sveltekit-flash-message/server'; -import type { LayoutServerLoad } from '../$types'; -// import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils'; -// import { lucia } from '$lib/server/auth'; +import { loadFlash } from 'sveltekit-flash-message/server' +import type { LayoutServerLoad } from '../$types' export const load: LayoutServerLoad = loadFlash(async (event) => { - const { url, locals, cookies } = event; - const authedUser = await locals.getAuthedUser(); - - // if (userNotFullyAuthenticated(user, session)) { - // await lucia.invalidateSession(locals.session!.id!); - // const sessionCookie = lucia.createBlankSessionCookie(); - // cookies.set(sessionCookie.name, sessionCookie.value, { - // path: '.', - // ...sessionCookie.attributes, - // }); - // } + const { url, locals } = event + const authedUser = await locals.getAuthedUser() return { url: url.pathname, - // user: userFullyAuthenticated(user, session) ? locals.user : null, - user: authedUser, - }; -}); + authedUser, + } +}) diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 49d18d5..aaab874 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -1,37 +1,39 @@ -
+
+
-
- {@render children()} -
+
+ {@render children()} +
-
+
+
\ No newline at end of file +{@render children()} + \ No newline at end of file