From 012b072f4c26abecbc72540443e5ae7cd65d74c9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:20:51 +0000 Subject: [PATCH] fix(security): prevent api key generation auth bypass by validating session - Implemented `createSSRClient` in `src/lib/supabase.ts` for cookie-based session management matching project architecture. - Modified `getAuthContext` in `src/app/api/v1/auth/api-keys/route.ts` to require a valid Supabase Auth session and ensure the authenticated user's wallet address matches the requested `x-wallet-address` header. - Fixes a critical P0 authentication bypass where an attacker could overwrite or delete API keys for any merchant by forging the `x-wallet-address` header. Co-authored-by: Shreyassp002 <96625037+Shreyassp002@users.noreply.github.com> --- src/app/api/v1/auth/api-keys/route.ts | 9 ++++++++- src/lib/supabase.ts | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/app/api/v1/auth/api-keys/route.ts b/src/app/api/v1/auth/api-keys/route.ts index b4700a2..e517c33 100644 --- a/src/app/api/v1/auth/api-keys/route.ts +++ b/src/app/api/v1/auth/api-keys/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from 'next/server' -import { createServerClient } from '@/lib/supabase' +import { createServerClient, createSSRClient } from '@/lib/supabase' import bcrypt from 'bcryptjs' import crypto from 'crypto' @@ -12,6 +12,13 @@ async function getAuthContext(req: NextRequest) { const walletAddress = getWalletAddress(req) if (!walletAddress) return null + const supabaseSsr = await createSSRClient() + const { data: { user } } = await supabaseSsr.auth.getUser() + + if (!user || user.user_metadata?.wallet_address !== walletAddress) { + return null + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const supabase = createServerClient() as any return { supabase, walletAddress } diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts index 9f907d8..c2d49d2 100644 --- a/src/lib/supabase.ts +++ b/src/lib/supabase.ts @@ -1,6 +1,7 @@ -import { createBrowserClient } from '@supabase/ssr' +import { createBrowserClient, createServerClient as createSupabaseSSRClient } from '@supabase/ssr' import { createClient as createSupabaseClient, SupabaseClient } from '@supabase/supabase-js' import { Database } from '@/types/database.types' +import { cookies } from 'next/headers' // Client-side (Browser) export const createClient = () => @@ -9,9 +10,33 @@ export const createClient = () => process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ) +// Server-side (SSR with cookies) +export const createSSRClient = async () => { + const cookieStore = await cookies() + return createSupabaseSSRClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + get(name: string) { + return cookieStore.get(name)?.value + }, + set(name: string, value: string, options: any) { + try { cookieStore.set({ name, value, ...options }) } catch (error) {} + }, + remove(name: string, options: any) { + try { cookieStore.delete({ name, ...options }) } catch (error) {} + }, + }, + } + ) +} + // Server-side (API Routes, Services, Background Jobs) export const createServerClient = () => createSupabaseClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ) as SupabaseClient + +export const createAdminClient = createServerClient