From 2eee642913cb0657f158fb5bac19fd5a46e37497 Mon Sep 17 00:00:00 2001 From: roshankumar0036singh Date: Thu, 28 May 2026 18:15:20 +0530 Subject: [PATCH 1/2] fix: remove duplicate OAuth callback route registrations in auth.ts - Removed duplicate nested app.get('/github/callback') that was registered inside an outer callback handler (caused by a bad merge) - Removed duplicate nested app.get('/google/callback') same issue - Removed dead code blocks (stray authUrl + redirect calls outside handlers) that were left over after the duplicate outer wrappers were stripped - All routes (/github, /github/callback, /google, /google/callback, /me, /logout) are now correctly registered at the top level within authRoutes plugin scope --- apps/backend/src/routes/auth.ts | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts index fe0e0f3f..b57226d0 100644 --- a/apps/backend/src/routes/auth.ts +++ b/apps/backend/src/routes/auth.ts @@ -59,24 +59,9 @@ export async function authRoutes(app: FastifyInstance) { state, }); const authUrl = `${GITHUB_AUTH_URL}?${params}`; + app.log.debug({ provider: 'github' }, 'OAuth redirect initiated'); return reply.redirect(authUrl); }); - const authUrl = `${GITHUB_AUTH_URL}?${params}`; - app.log.debug({ provider: 'github' }, 'OAuth redirect initiated'); - return reply.redirect(authUrl); -}); - -app.get('/github/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { - const { code, state } = request.query; - - // ── CSRF check ────────────────────────────────────────────────────────────── - const storedState = (request.cookies as any)?.oauth_state; - if (!state || !storedState || state !== storedState) { - return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' }); - } - // Clear the state cookie immediately — prevents replay - reply.clearCookie('oauth_state', { path: '/' }); - // ──────────────────────────────────────────────────────────────────────────── app.get('/github/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { const { code, state } = request.query; @@ -225,23 +210,9 @@ app.get('/github/callback', async (request: FastifyRequest<{ Querystring: OAuthC access_type: 'offline', }); const authUrl = `${GOOGLE_AUTH_URL}?${params}`; + app.log.debug({ provider: 'google' }, 'OAuth redirect initiated'); return reply.redirect(authUrl); }); - const authUrl = `${GOOGLE_AUTH_URL}?${params}`; - app.log.debug({ provider: 'google' }, 'OAuth redirect initiated'); - return reply.redirect(authUrl); -}); - - app.get('/google/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { - const { code, state } = request.query; - - // ── CSRF check ────────────────────────────────────────────────────────────── - const storedState = (request.cookies as any)?.oauth_state; - if (!state || !storedState || state !== storedState) { - return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' }); - } - reply.clearCookie('oauth_state', { path: '/' }); - // ──────────────────────────────────────────────────────────────────────────── app.get('/google/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { const { code, state } = request.query; From abf88fb6d497ecd5535a2e248aa2ce1f0cb849fc Mon Sep 17 00:00:00 2001 From: roshankumar0036singh Date: Thu, 28 May 2026 18:21:54 +0530 Subject: [PATCH 2/2] fix: resolve OAuth CSRF vulnerabilities and add cookie types --- apps/backend/src/routes/auth.ts | 20 ++++++++------------ apps/backend/src/types/fastify.d.ts | 8 ++++++++ 2 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 apps/backend/src/types/fastify.d.ts diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts index b57226d0..0fa89c13 100644 --- a/apps/backend/src/routes/auth.ts +++ b/apps/backend/src/routes/auth.ts @@ -66,13 +66,11 @@ export async function authRoutes(app: FastifyInstance) { app.get('/github/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { const { code, state } = request.query; - // Validate state to prevent CSRF attacks - const storedState = (request.cookies as any).oauth_state; - reply.clearCookie('oauth_state', { path: '/' }); - - if (!storedState || !state || state !== storedState) { - return reply.status(400).send({ error: 'Invalid OAuth state parameter.' }); + const storedState = request.cookies?.oauth_state; + if (!state || !storedState || state !== storedState) { + return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' }); } + reply.clearCookie('oauth_state', { path: '/' }); if (!code) { return reply.status(400).send({ error: 'Missing authorization code' }); @@ -217,13 +215,11 @@ export async function authRoutes(app: FastifyInstance) { app.get('/google/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => { const { code, state } = request.query; - // Validate state to prevent CSRF attacks - const storedState = (request.cookies as any).oauth_state; - reply.clearCookie('oauth_state', { path: '/' }); - - if (!storedState || !state || state !== storedState) { - return reply.status(400).send({ error: 'Invalid OAuth state parameter.' }); + const storedState = request.cookies?.oauth_state; + if (!state || !storedState || state !== storedState) { + return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' }); } + reply.clearCookie('oauth_state', { path: '/' }); if (!code) { return reply.status(400).send({ error: 'Missing authorization code' }); diff --git a/apps/backend/src/types/fastify.d.ts b/apps/backend/src/types/fastify.d.ts new file mode 100644 index 00000000..8e7aee95 --- /dev/null +++ b/apps/backend/src/types/fastify.d.ts @@ -0,0 +1,8 @@ +import '@fastify/cookie'; +import { FastifyRequest } from 'fastify'; + +declare module 'fastify' { + interface FastifyRequest { + cookies: Record; + } +}