From 2074f8d8f106c19c9b1dd654a24b7cdd3b57dc86 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Wed, 8 Oct 2025 17:24:46 +0200 Subject: [PATCH] fix(shared/auth/icon): Fixed lots of small things Icons: Fixed docker-compose to force JWT_SECRET for now Auth: Fixed Guest Login to actually work Auth: Added `Login as Guest` in the login_demo page Shared: Fixed db/user + uuid modules --- docker-compose.yml | 1 + src/@shared/src/database/mixin/user.ts | 12 ++++++++---- src/@shared/src/utils/uuid.ts | 22 ++++++++++++++++++++++ src/auth/extra/login_demo.html | 1 + src/auth/extra/login_demo.js | 11 +++++++++++ src/auth/src/routes/guestLogin.ts | 15 ++++++++------- 6 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 src/@shared/src/utils/uuid.ts diff --git a/docker-compose.yml b/docker-compose.yml index a3db5a3..9a130f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,7 @@ services: - images-volume:/volumes/store - sqlite-volume:/volumes/database environment: + - JWT_SECRET=KRUGKIDROVUWG2ZAMJZG653OEBTG66BANJ2W24DTEBXXMZLSEB2GQZJANRQXU6JA - USER_ICONS_STORE=/volumes/store - DATABASE_DIR=/volumes/database diff --git a/src/@shared/src/database/mixin/user.ts b/src/@shared/src/database/mixin/user.ts index ccda5e7..7cb3ef3 100644 --- a/src/@shared/src/database/mixin/user.ts +++ b/src/@shared/src/database/mixin/user.ts @@ -2,7 +2,7 @@ import type { Database, SqliteReturn } from './_base'; import { Otp } from '@shared/auth'; import { isNullish } from '@shared/utils'; import * as bcrypt from 'bcrypt'; -import { UUID } from 'uuidv7'; +import { UUID, newUUID } from '@shared/utils/uuid'; // never use this directly @@ -56,12 +56,13 @@ export const UserImpl: Omit = { * * @returns The user struct */ - async createUser(this: IUserDb, name: string, password: string | undefined): Promise { + async createUser(this: IUserDb, name: string, password: string | undefined, guest: boolean = false): Promise { password = await hashPassword(password); + const id = newUUID(); return userFromRow( this.prepare( - 'INSERT OR FAIL INTO user (name, password) VALUES (@name, @password) RETURNING *', - ).get({ name, password }) as (Partial | undefined), + 'INSERT OR FAIL INTO user (id, name, password, guest) VALUES (@id, @name, @password, @guest) RETURNING *', + ).get({ id, name, password, guest: guest ? 1 : 0 }) as (Partial | undefined), ); }, @@ -110,6 +111,7 @@ export type User = { readonly name: string; readonly password?: string; readonly otp?: string; + readonly guest: boolean; }; export async function verifyUserPassword( @@ -148,10 +150,12 @@ function userFromRow(row?: Partial): User | undefined { if (isNullish(row)) return undefined; if (isNullish(row.id)) return undefined; if (isNullish(row.name)) return undefined; + if (isNullish(row.guest)) return undefined; return { id: row.id, name: row.name, password: row.password ?? undefined, otp: row.otp ?? undefined, + guest: !!(row.guest ?? true), }; } diff --git a/src/@shared/src/utils/uuid.ts b/src/@shared/src/utils/uuid.ts new file mode 100644 index 0000000..20debf0 --- /dev/null +++ b/src/@shared/src/utils/uuid.ts @@ -0,0 +1,22 @@ +import * as uuidv7 from 'uuidv7'; + +export type UUID = `${string}-${string}-${string}-${string}-${string}` & { readonly __brand: unique symbol }; +export default UUID; + +export function newUUID(): UUID { + return uuidv7.uuidv7() as UUID; +} + +export function isUUID(s: string): s is UUID { + try { + uuidv7.UUID.parse(s); + return true; + } + catch { + return false; + } +} + +export function parseUUID(s: string): UUID { + return uuidv7.UUID.parse(s).toString() as UUID; +} diff --git a/src/auth/extra/login_demo.html b/src/auth/extra/login_demo.html index c7dae28..ed4dadf 100644 --- a/src/auth/extra/login_demo.html +++ b/src/auth/extra/login_demo.html @@ -20,6 +20,7 @@

+

diff --git a/src/auth/extra/login_demo.js b/src/auth/extra/login_demo.js index 451986a..c7e7e99 100644 --- a/src/auth/extra/login_demo.js +++ b/src/auth/extra/login_demo.js @@ -11,6 +11,7 @@ const iOtp = document.querySelector('#i-otp'); const bOtpSend = document.querySelector('#b-otpSend'); const bLogin = document.querySelector('#b-login'); +const bLoginGuest = document.querySelector('#b-login-guest'); const bLogout = document.querySelector('#b-logout'); const bSignin = document.querySelector('#b-signin'); const bWhoami = document.querySelector('#b-whoami'); @@ -27,6 +28,16 @@ function setResponse(obj) { } let otpToken = null; +bLoginGuest.addEventListener('click', async () => { + const res = await fetch('/api/auth/guest', { method: 'POST' }); + const json = await res.json(); + + setResponse(json); + if (json.kind === 'success') { + if (json?.payload?.token) {document.cookie = `token=${json?.payload?.token}`;} + } +}); + bOtpSend.addEventListener('click', async () => { const res = await fetch('/api/auth/otp', { method: 'POST', body: JSON.stringify({ code: iOtp.value, token: otpToken }), headers }); const json = await res.json(); diff --git a/src/auth/src/routes/guestLogin.ts b/src/auth/src/routes/guestLogin.ts index 713d032..61db359 100644 --- a/src/auth/src/routes/guestLogin.ts +++ b/src/auth/src/routes/guestLogin.ts @@ -4,8 +4,8 @@ import { Static, Type } from '@sinclair/typebox'; import { typeResponse, makeResponse, isNullish } from '@shared/utils'; export const GuestLoginRes = Type.Union([ - typeResponse('failed', 'login.failed.generic'), - typeResponse('success', 'login.success', { + typeResponse('failed', ['guestLogin.failed.generic.unknown', 'guestLogin.failed.generic.error']), + typeResponse('success', 'guestLogin.success', { token: Type.String({ description: 'JWT that represent a logged in user', }), @@ -38,14 +38,15 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { true, ); if (isNullish(user)) { - return makeResponse('failed', 'login.failed.generic'); + return makeResponse('failed', 'guestLogin.failed.generic.unknown'); } - return makeResponse('success', 'login.success', { - token: this.signJwt('auth', user.id), + return makeResponse('success', 'guestLogin.success', { + token: this.signJwt('auth', user.id.toString()), }); } - catch { - return makeResponse('failed', 'login.failed.generic'); + catch (e: unknown) { + fastify.log.error(e); + return makeResponse('failed', 'guestLogin.failed.generic.error'); } }, );