From 1996b97e99d56191841bcea6c3d3c7990c55054f Mon Sep 17 00:00:00 2001 From: DecDuck Date: Mon, 24 Mar 2025 12:50:21 +1100 Subject: [PATCH] refactor: use hash directly in authmek and version field on authmek --- .../migration.sql | 2 + prisma/schema/auth.prisma | 1 + server/api/v1/auth/signin/simple.post.ts | 54 +++++++++---------- server/api/v1/auth/signup/simple.post.ts | 11 +--- server/internal/security/simple.ts | 7 --- 5 files changed, 30 insertions(+), 45 deletions(-) create mode 100644 prisma/migrations/20250324014736_add_auth_mek_version/migration.sql diff --git a/prisma/migrations/20250324014736_add_auth_mek_version/migration.sql b/prisma/migrations/20250324014736_add_auth_mek_version/migration.sql new file mode 100644 index 0000000..a6bdfb7 --- /dev/null +++ b/prisma/migrations/20250324014736_add_auth_mek_version/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "LinkedAuthMec" ADD COLUMN "version" INTEGER NOT NULL DEFAULT 1; diff --git a/prisma/schema/auth.prisma b/prisma/schema/auth.prisma index bf862dd..386afbc 100644 --- a/prisma/schema/auth.prisma +++ b/prisma/schema/auth.prisma @@ -7,6 +7,7 @@ model LinkedAuthMec { mec AuthMec enabled Boolean @default(true) + version Int @default(1) credentials Json user User @relation(fields: [userId], references: [id]) diff --git a/server/api/v1/auth/signin/simple.post.ts b/server/api/v1/auth/signin/simple.post.ts index 481c280..dcc2238 100644 --- a/server/api/v1/auth/signin/simple.post.ts +++ b/server/api/v1/auth/signin/simple.post.ts @@ -5,7 +5,6 @@ import prisma from "~/server/internal/db/database"; import { checkHashArgon2, checkHashBcrypt, - simpleAuth, } from "~/server/internal/security/simple"; import sessionHandler from "~/server/internal/session"; @@ -43,16 +42,18 @@ export default defineEventHandler(async (h3) => { statusCode: 401, statusMessage: "Invalid username or password.", }); - else if (!authMek.user.enabled) + + if (!authMek.user.enabled) throw createError({ statusCode: 403, statusMessage: "Invalid or disabled account. Please contact the server administrator.", }); - // if using old auth schema - if (Array.isArray(authMek.credentials)) { - const hash = authMek.credentials.at(1)?.toString(); + // LEGACY bcrypt + if (authMek.version == 1) { + const credentials = authMek.credentials as JsonArray | null; + const hash = credentials?.at(1)?.toString(); if (!hash) throw createError({ @@ -69,30 +70,25 @@ export default defineEventHandler(async (h3) => { // TODO: send user to forgot password screen or something to force them to change their password to new system await sessionHandler.setUserId(h3, authMek.userId, rememberMe); - return { result: true, userId: authMek.userId }; - } else { - // using new (modern) login flow - - const creds = simpleAuth(authMek.credentials); - if (creds instanceof type.errors) { - // hover out.summary to see validation errors - console.error(creds.summary); - - throw createError({ - statusCode: 403, - statusMessage: - "Invalid password state. Please contact the server administrator.", - }); - } - - if (!(await checkHashArgon2(password, creds.password))) - throw createError({ - statusCode: 401, - statusMessage: "Invalid username or password.", - }); - - await sessionHandler.setUserId(h3, authMek.userId, rememberMe); - return { result: true, userId: authMek.userId }; } + + // V2: argon2 + const hash = authMek.credentials as string | undefined; + if (!hash || typeof hash !== "string") + throw createError({ + statusCode: 500, + statusMessage: + "Invalid password state. Please contact the server administrator.", + }); + + if (!(await checkHashArgon2(password, hash))) + throw createError({ + statusCode: 401, + statusMessage: "Invalid username or password.", + }); + + await sessionHandler.setUserId(h3, authMek.userId, rememberMe); + + return { result: true, userId: authMek.userId }; }); diff --git a/server/api/v1/auth/signup/simple.post.ts b/server/api/v1/auth/signup/simple.post.ts index b854f0d..8c91bb6 100644 --- a/server/api/v1/auth/signup/simple.post.ts +++ b/server/api/v1/auth/signup/simple.post.ts @@ -2,8 +2,6 @@ import { AuthMec, Invitation } from "@prisma/client"; import prisma from "~/server/internal/db/database"; import { createHashArgon2, - simpleAuth, - SimpleAuthType, } from "~/server/internal/security/simple"; import { v4 as uuidv4 } from "uuid"; import * as jdenticon from "jdenticon"; @@ -70,17 +68,12 @@ export default defineEventHandler(async (h3) => { {}, [`internal:read`, `${userId}:write`] ); - - const creds: SimpleAuthType = { - version: "v1.0.0", - password: await createHashArgon2(user.password), - }; - const [linkMec] = await prisma.$transaction([ prisma.linkedAuthMec.create({ data: { mec: AuthMec.Simple, - credentials: creds, + credentials: await createHashArgon2(user.password), + version: 2, user: { create: { id: userId, diff --git a/server/internal/security/simple.ts b/server/internal/security/simple.ts index 5d5f7db..55c91f5 100644 --- a/server/internal/security/simple.ts +++ b/server/internal/security/simple.ts @@ -2,13 +2,6 @@ import bcrypt from "bcryptjs"; import * as argon2 from "argon2"; import { type } from "arktype"; -export const simpleAuth = type({ - version: "string.semver", - password: "string", -}); - -export type SimpleAuthType = typeof simpleAuth.infer; - export async function checkHashBcrypt(password: string, hash: string) { return await bcrypt.compare(password, hash); }