diff --git a/apps/web/app/api/auth/callback/route.ts b/apps/web/app/api/auth/callback/route.ts index c1f8149..7b122e3 100644 --- a/apps/web/app/api/auth/callback/route.ts +++ b/apps/web/app/api/auth/callback/route.ts @@ -14,7 +14,11 @@ export async function GET(req: NextRequest) { const storedState = req.cookies.get('cp_state')?.value; const codeVerifier = req.cookies.get('cp_pkce')?.value; - const returnTo = req.cookies.get('cp_return')?.value ?? '/'; + let returnTo = req.cookies.get('cp_return')?.value ?? '/'; + // Prevent open redirect — only allow relative paths + if (!returnTo.startsWith('/') || returnTo.startsWith('//')) { + returnTo = '/'; + } const fail = (msg: string) => NextResponse.redirect(new URL(`/?auth_error=${encodeURIComponent(msg)}`, APP_URL)); diff --git a/apps/web/app/api/auth/coinpay/route.ts b/apps/web/app/api/auth/coinpay/route.ts index ab17cf7..2fac34c 100644 --- a/apps/web/app/api/auth/coinpay/route.ts +++ b/apps/web/app/api/auth/coinpay/route.ts @@ -9,7 +9,11 @@ function base64url(buf: ArrayBuffer | Uint8Array): string { } export async function GET(req: NextRequest) { - const returnTo = req.nextUrl.searchParams.get('returnTo') ?? '/'; + let returnTo = req.nextUrl.searchParams.get('returnTo') ?? '/'; + // Prevent open redirect — only allow relative paths + if (!returnTo.startsWith('/') || returnTo.startsWith('//')) { + returnTo = '/'; + } // PKCE const verifierBytes = crypto.getRandomValues(new Uint8Array(32));