From db8f3d6a094e893b2c56d24db83ccb396f47bdf3 Mon Sep 17 00:00:00 2001 From: Bernd Storath Date: Fri, 12 Jun 2026 11:49:34 +0200 Subject: [PATCH] improvements --- src/server/api/auth/verify-2fa.post.ts | 18 +++++++++--- src/server/api/session.get.ts | 8 +++++- .../database/repositories/user/service.ts | 28 +++++++++++++------ src/shared/utils/permissions.ts | 5 +++- src/test/unit/password.spec.ts | 4 +++ 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/server/api/auth/verify-2fa.post.ts b/src/server/api/auth/verify-2fa.post.ts index d988d28f..2fa19d86 100644 --- a/src/server/api/auth/verify-2fa.post.ts +++ b/src/server/api/auth/verify-2fa.post.ts @@ -9,7 +9,7 @@ export default defineEventHandler(async (event) => { event, validateZod(Verify2faSchema, event) ); - const session = await useWGSession(event, false); + const session = await useWGSession(event); const pendingLogin = session.data.pendingLogin; if (!pendingLogin) { @@ -19,13 +19,23 @@ export default defineEventHandler(async (event) => { }); } - const isValid = await Database.users.validateTotpCode( + const totpStatus = await Database.users.validateTotpCode( pendingLogin.userId, totpCode ); - if (!isValid) { - return { status: 'INVALID_TOTP_CODE' as const }; + switch (totpStatus) { + case 'INVALID_TOTP_CODE': + return { status: 'INVALID_TOTP_CODE' as const }; + case 'USER_DISABLED': + throw createError({ + statusCode: 401, + statusMessage: 'User disabled', + }); + case 'success': + break; + default: + assertUnreachable(totpStatus); } await session.update({ diff --git a/src/server/api/session.get.ts b/src/server/api/session.get.ts index 3e50a2a0..7cb38bb6 100644 --- a/src/server/api/session.get.ts +++ b/src/server/api/session.get.ts @@ -1,7 +1,7 @@ export default defineEventHandler(async (event) => { const session = await useWGSession(event); - if (!session.data.userId) { + if (!session.data.userId || session.data.pendingLogin) { // not logged in throw createError({ statusCode: 401, @@ -16,6 +16,12 @@ export default defineEventHandler(async (event) => { statusMessage: 'Not found in Database', }); } + if (!user.enabled) { + throw createError({ + statusCode: 403, + statusMessage: 'User is disabled', + }); + } return { id: user.id, diff --git a/src/server/database/repositories/user/service.ts b/src/server/database/repositories/user/service.ts index 8f4afccf..e3526315 100644 --- a/src/server/database/repositories/user/service.ts +++ b/src/server/database/repositories/user/service.ts @@ -195,6 +195,10 @@ export class UserService { return { success: false, error: 'INCORRECT_CREDENTIALS' }; } + if (!txUser.enabled) { + return { success: false, error: 'USER_DISABLED' }; + } + if (txUser.totpVerified) { return { success: false, @@ -203,10 +207,6 @@ export class UserService { }; } - if (!txUser.enabled) { - return { success: false, error: 'USER_DISABLED' }; - } - return { success: true, user: txUser }; }); } @@ -272,14 +272,24 @@ export class UserService { .execute(); if (!txUser || !txUser.totpVerified || !txUser.totpKey) { - return false; + return 'INVALID_TOTP_CODE' as const; } const totp = this.#createTotp({ username: txUser.username, totpKey: txUser.totpKey, }); - return totp.validate({ token: code, window: 1 }) !== null; + const isValid = totp.validate({ token: code, window: 1 }) !== null; + + if (!isValid) { + return 'INVALID_TOTP_CODE' as const; + } + + if (!txUser.enabled) { + return 'USER_DISABLED' as const; + } + + return 'success' as const; } /** @@ -305,6 +315,9 @@ export class UserService { .execute(); if (userById) { + if (!userById.enabled) { + return { success: false, error: 'USER_DISABLED' }; + } if (userById.totpVerified) { return { success: false, @@ -312,9 +325,6 @@ export class UserService { userId: userById.id, }; } - if (!userById.enabled) { - return { success: false, error: 'USER_DISABLED' }; - } return { success: true, user: userById }; } diff --git a/src/shared/utils/permissions.ts b/src/shared/utils/permissions.ts index dd0a50f8..e9f5fc18 100644 --- a/src/shared/utils/permissions.ts +++ b/src/shared/utils/permissions.ts @@ -144,7 +144,10 @@ export function hasPermissionsWithData( const isAllowed = hasPermissions(user, resource, action, data); if (!isAllowed) { - throw new Error('Permission denied'); + throw createError({ + statusCode: 403, + statusMessage: 'Permission denied', + }); } return isAllowed; diff --git a/src/test/unit/password.spec.ts b/src/test/unit/password.spec.ts index 855c5f24..5f404530 100644 --- a/src/test/unit/password.spec.ts +++ b/src/test/unit/password.spec.ts @@ -17,4 +17,8 @@ describe('password', () => { expect(isValidPasswordHash(hash.replace('argon2', 'argon3'))).toBe(false); }); + + test('missing password hash is never valid', async () => { + await expect(isPasswordValid('password', null)).resolves.toBe(false); + }); });