From d8b3fa439c0a20f9de18274d1d17abdd50838860 Mon Sep 17 00:00:00 2001 From: ramnnnn2006 Date: Fri, 12 Jun 2026 20:16:29 +0530 Subject: [PATCH 1/5] add AuthenticatedUser type for the jwt user payload --- apps/backend/src/types/fastify.d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/backend/src/types/fastify.d.ts b/apps/backend/src/types/fastify.d.ts index 8e7aee95..902f3849 100644 --- a/apps/backend/src/types/fastify.d.ts +++ b/apps/backend/src/types/fastify.d.ts @@ -1,6 +1,18 @@ import '@fastify/cookie'; +import '@fastify/jwt'; import { FastifyRequest } from 'fastify'; +export interface AuthenticatedUser { + id: string; + username: string; +} + +declare module '@fastify/jwt' { + interface FastifyJWT { + user: AuthenticatedUser; + } +} + declare module 'fastify' { interface FastifyRequest { cookies: Record; From 7382edff5367ee3651ec1526479087f2db4c2746 Mon Sep 17 00:00:00 2001 From: ramnnnn2006 Date: Fri, 12 Jun 2026 20:28:28 +0530 Subject: [PATCH 2/5] refactor(types): declare authenticate decorator on FastifyInstance --- apps/backend/src/types/fastify.d.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/backend/src/types/fastify.d.ts b/apps/backend/src/types/fastify.d.ts index 902f3849..faeddd2a 100644 --- a/apps/backend/src/types/fastify.d.ts +++ b/apps/backend/src/types/fastify.d.ts @@ -1,6 +1,6 @@ import '@fastify/cookie'; import '@fastify/jwt'; -import { FastifyRequest } from 'fastify'; +import { FastifyReply, FastifyRequest } from 'fastify'; export interface AuthenticatedUser { id: string; @@ -17,4 +17,8 @@ declare module 'fastify' { interface FastifyRequest { cookies: Record; } + + interface FastifyInstance { + authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise; + } } From a8e6f0f40ff41e534d50f39b7062ef13e0bf84a8 Mon Sep 17 00:00:00 2001 From: ramnnnn2006 Date: Fri, 12 Jun 2026 20:31:40 +0530 Subject: [PATCH 3/5] refactor(app): type the authenticate decorator instead of any --- apps/backend/src/app.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts index 6116b91b..44842088 100644 --- a/apps/backend/src/app.ts +++ b/apps/backend/src/app.ts @@ -7,7 +7,7 @@ import helmet from '@fastify/helmet'; import jwt from '@fastify/jwt'; import multipart from '@fastify/multipart'; import rateLimit from '@fastify/rate-limit'; -import Fastify, {type FastifyInstance} from 'fastify'; +import Fastify, {type FastifyInstance, type FastifyReply, type FastifyRequest} from 'fastify'; import { prismaPlugin } from './plugins/prisma.js'; import { redisPlugin } from './plugins/redis.js'; @@ -24,6 +24,8 @@ import { teamRoutes } from './routes/team.js'; import { extractRawJwt, blocklistKey } from './utils/jwt.js'; import { validateEnv } from './utils/validateEnv.js'; +import type { AuthenticatedUser } from './types/fastify.js'; + const __dirname = path.dirname(fileURLToPath(import.meta.url)); export async function buildApp():Promise { @@ -104,7 +106,7 @@ export async function buildApp():Promise { // Checks the Redis blocklist before calling jwtVerify so that a logged-out // token is rejected immediately even if it has not yet expired. // The blocklist check is skipped when Redis is not registered (test env). - app.decorate('authenticate', async function (request: any, reply: any) { + app.decorate('authenticate', async function (request: FastifyRequest, reply: FastifyReply) { try { if (app.hasDecorator('redis')) { const raw = extractRawJwt(request); @@ -122,7 +124,7 @@ export async function buildApp():Promise { } } // Assign verified payload to request.user (upstream addition). - const payload = await request.jwtVerify(); + const payload = await request.jwtVerify(); if (payload) { request.user = payload; } } catch (_err) { return reply.status(401).send({ error: 'Unauthorized' }); From 51ed7c6f0ba8e68a03331b76b75f1cf8e9cd5fa1 Mon Sep 17 00:00:00 2001 From: ramnnnn2006 Date: Fri, 12 Jun 2026 20:43:23 +0530 Subject: [PATCH 4/5] refactor(auth): drop any cast on request.user in /me route --- apps/backend/src/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts index 11351267..964534d1 100644 --- a/apps/backend/src/routes/auth.ts +++ b/apps/backend/src/routes/auth.ts @@ -613,7 +613,7 @@ export async function authRoutes(app: FastifyInstance): Promise { // eslint-disable-next-line @typescript-eslint/unbound-method preHandler: [app.authenticate], }, async (request: FastifyRequest, reply: FastifyReply) => { - const userId = (request.user as any).id; + const userId = request.user.id; const user = await app.prisma.user.findUnique({ where: { id: userId }, select: { From 4a823bf53c8a7806e610a0e9c73a227b16224e27 Mon Sep 17 00:00:00 2001 From: ramnnnn2006 Date: Sat, 13 Jun 2026 15:03:20 +0530 Subject: [PATCH 5/5] refactor(auth): drop remaining any cast in secure logout route --- apps/backend/src/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts index 964534d1..3bc39ad4 100644 --- a/apps/backend/src/routes/auth.ts +++ b/apps/backend/src/routes/auth.ts @@ -676,7 +676,7 @@ export async function authRoutes(app: FastifyInstance): Promise { await app.redis.set(blocklistKey(raw), '1', 'EX', ttl); } catch (err) { // Non-fatal: log and continue. The token will expire on its own. - app.log.warn({ err, userId: (request.user as any)?.id }, 'Redis blocklist write failed during logout — token will expire naturally'); + app.log.warn({ err, userId: request.user?.id }, 'Redis blocklist write failed during logout — token will expire naturally'); } } } else {