From 92170e44f48ba474d690eb275cbd515865841acc Mon Sep 17 00:00:00 2001 From: Moheeb Zara Date: Mon, 18 May 2026 18:31:06 -0700 Subject: [PATCH] fix(auth): validate /auth/callback next param against open-redirect (D3) next was appended to origin unvalidated; next=//evil.com or /\evil.com redirected off-site post-login. Now only a same-origin absolute path (single leading slash, no //, no /\) is honored, else /dashboard. --- app/auth/callback/route.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts index b2a5183..f88a7ad 100644 --- a/app/auth/callback/route.ts +++ b/app/auth/callback/route.ts @@ -1,10 +1,21 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' +// Only a same-origin absolute path is an allowed post-login target. +// Rejects open-redirect vectors: protocol-relative (//evil.com), +// backslash tricks (/\evil.com), and any scheme/host. +function safeNext(raw: string | null): string { + const fallback = '/dashboard' + if (!raw || !raw.startsWith('/') || raw.startsWith('//') || raw.startsWith('/\\')) { + return fallback + } + return raw +} + export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url) const code = searchParams.get('code') - const next = searchParams.get('next') ?? '/dashboard' + const next = safeNext(searchParams.get('next')) if (code) { const supabase = await createClient()