From 7caf314ce172a01a1a8b2f8e167a3d62e1cd4aaf Mon Sep 17 00:00:00 2001 From: diced Date: Sun, 19 Apr 2026 21:49:19 -0700 Subject: [PATCH] fix: session serialization errors --- src/client/pages/folder/[id]/index.tsx | 7 ------- src/lib/api/errors.ts | 1 + src/server/middleware/user.ts | 11 +++-------- src/server/routes/api/user/sessions.ts | 10 ++++------ src/server/session.ts | 10 ++++++++++ 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/client/pages/folder/[id]/index.tsx b/src/client/pages/folder/[id]/index.tsx index 6c7ccc13..8d12adaf 100644 --- a/src/client/pages/folder/[id]/index.tsx +++ b/src/client/pages/folder/[id]/index.tsx @@ -117,13 +117,6 @@ export function Component() { return ( <> - {/* setCurrent(open ? (currentFile?.id ?? null) : null)} - file={currentFile} - reduce - sequenced - /> */} setCurrent(open ? (currentFile?.id ?? null) : null)} diff --git a/src/lib/api/errors.ts b/src/lib/api/errors.ts index 7bb5bb40..9f1628eb 100644 --- a/src/lib/api/errors.ts +++ b/src/lib/api/errors.ts @@ -69,6 +69,7 @@ export const API_ERRORS = { 2001: 'Invalid token', 2002: 'Not logged in', 2003: 'OAuth provider is not configured (or misconfigured)', + 2004: 'Invalid login steps (cookie relying on token)', // 3xxx, permission errors 3000: 'Admin only', diff --git a/src/server/middleware/user.ts b/src/server/middleware/user.ts index 327598c5..6b370c5c 100644 --- a/src/server/middleware/user.ts +++ b/src/server/middleware/user.ts @@ -22,13 +22,12 @@ export function parseUserToken( ): string | null { if (!encryptedToken) { if (noThrow) return null; - throw { error: 'no token' }; + throw new ApiError(2001); } const decryptedToken = decryptToken(encryptedToken, config.core.secret); if (!decryptedToken) { if (noThrow) return null; - // throw { error: 'could not decrypt token' }; throw new ApiError(2001); } @@ -56,12 +55,7 @@ export async function userMiddleware(req: FastifyRequest, res: FastifyReply) { const authorization = req.headers.authorization; if (authorization) { - try { - // eslint-disable-next-line no-var - var token = parseUserToken(authorization); - } catch (e) { - throw e; - } + const token = parseUserToken(authorization); const user = await prisma.user.findFirst({ where: { @@ -77,6 +71,7 @@ export async function userMiddleware(req: FastifyRequest, res: FastifyReply) { } const session = await getSession(req, res); + if (session.tokenAuth) throw new ApiError(2004); if (!session.id || !session.sessionId) throw new ApiError(2000); diff --git a/src/server/routes/api/user/sessions.ts b/src/server/routes/api/user/sessions.ts index 850e469d..996b4d92 100644 --- a/src/server/routes/api/user/sessions.ts +++ b/src/server/routes/api/user/sessions.ts @@ -24,7 +24,7 @@ export default typedPlugin( 'List the current browser session and other active sessions for the authenticated user.', response: { 200: z.object({ - current: userSessionSchema, + current: userSessionSchema.nullable(), other: z.array(userSessionSchema), }), }, @@ -37,10 +37,8 @@ export default typedPlugin( const currentDbSession = req.user.sessions.find((session) => session.id === currentSession.sessionId); - if (!currentDbSession) throw new ApiError(2000); - return res.send({ - current: currentDbSession, + current: currentDbSession ?? null, other: req.user.sessions.filter((session) => session.id !== currentSession.sessionId), }); }, @@ -57,7 +55,7 @@ export default typedPlugin( }), response: { 200: z.object({ - current: userSessionSchema, + current: userSessionSchema.nullable(), other: z.array(userSessionSchema), }), }, @@ -122,7 +120,7 @@ export default typedPlugin( }); return res.send({ - current: user.sessions.find((session) => session.id === currentSession.sessionId)!, + current: user.sessions.find((session) => session.id === currentSession.sessionId) ?? null, other: user.sessions.filter((session) => session.id !== currentSession.sessionId), }); }, diff --git a/src/server/session.ts b/src/server/session.ts index 941215e6..3ba4a12f 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2,9 +2,11 @@ import { detectClient, ZiplineClient } from '@/lib/api/detect'; import { config } from '@/lib/config'; import { prisma } from '@/lib/db'; import { randomCharacters } from '@/lib/random'; +import { parse } from 'cookie'; import { FastifyReply, FastifyRequest } from 'fastify'; import { IncomingMessage, ServerResponse } from 'http'; import { getIronSession, type SessionOptions } from 'iron-session'; +import { parseUserToken } from './middleware/user'; const cookieOptions: NonNullable = { // 2 weeks @@ -22,6 +24,7 @@ export type ZiplineSession = { client: ZiplineClient; pkceVerifier?: string; + tokenAuth?: boolean; }; export type ZiplineIronSession = Awaited>; @@ -47,6 +50,13 @@ export async function getSession( const headers = (req as FastifyRequest).headers || (req as IncomingMessage).headers; session.client = detectClient(>headers); + const cookies = parse(headers.cookie || ''); + + if (headers['authorization'] && !cookies['zipline_session']) { + const token = parseUserToken(headers['authorization'], true); + + if (token) session.tokenAuth = true; + } return session; }