From f47c9e58ce01b00e07b1083df100880810fd82a4 Mon Sep 17 00:00:00 2001 From: Marcio Bevervanso Date: Sun, 7 Jun 2026 22:01:52 -0300 Subject: [PATCH] fix: resolve OAuth redirects dynamically using client-facing headers --- src/app/api/auth/callback/route.ts | 12 +++++++----- src/app/api/auth/logout/route.ts | 4 +++- src/app/api/auth/meta/route.ts | 7 ++++--- src/lib/url.ts | 25 +++++++++++++++++++++++++ src/middleware.ts | 4 +++- 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 src/lib/url.ts diff --git a/src/app/api/auth/callback/route.ts b/src/app/api/auth/callback/route.ts index 38cbbdf..fcf853b 100644 --- a/src/app/api/auth/callback/route.ts +++ b/src/app/api/auth/callback/route.ts @@ -6,17 +6,19 @@ import { NextRequest, NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { setSessionCookies } from '@/lib/auth/session'; import { getSettings } from '@/lib/settings'; +import { getRequestBaseUrl } from '@/lib/url'; export async function GET(request: NextRequest) { const code = request.nextUrl.searchParams.get('code'); const error = request.nextUrl.searchParams.get('error'); + const baseUrl = getRequestBaseUrl(request); if (error || !code) { const errorDesc = request.nextUrl.searchParams.get('error_description') || 'Login cancelled'; return NextResponse.redirect( - new URL(`/login?error=${encodeURIComponent(errorDesc)}`, request.url) + new URL(`/login?error=${encodeURIComponent(errorDesc)}`, baseUrl) ); } @@ -32,7 +34,7 @@ export async function GET(request: NextRequest) { const redirectUri = process.env.META_REDIRECT_URI || - 'https://admanager.seureview.com.br/api/auth/callback'; + new URL('/api/auth/callback', baseUrl).toString(); if (!appId || !appSecret) { return NextResponse.redirect( @@ -40,7 +42,7 @@ export async function GET(request: NextRequest) { `/login?error=${encodeURIComponent( 'Credenciais Meta não configuradas no sistema.' )}`, - request.url + baseUrl ) ); } @@ -96,14 +98,14 @@ export async function GET(request: NextRequest) { }); // 5. Redirect to dashboard - return NextResponse.redirect(new URL('/dashboard', request.url)); + return NextResponse.redirect(new URL('/dashboard', baseUrl)); } catch (err) { console.error('[Auth] OAuth error:', err); return NextResponse.redirect( new URL( `/login?error=${encodeURIComponent('Erro na autenticação')}`, - request.url + baseUrl ) ); } diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index e32f642..099485f 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -5,9 +5,11 @@ import { NextRequest, NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { clearSessionCookies } from '@/lib/auth/session'; +import { getRequestBaseUrl } from '@/lib/url'; export async function POST(request: NextRequest) { const cookieStore = await cookies(); clearSessionCookies(cookieStore); - return NextResponse.redirect(new URL('/login', request.url)); + const baseUrl = getRequestBaseUrl(request); + return NextResponse.redirect(new URL('/login', baseUrl)); } diff --git a/src/app/api/auth/meta/route.ts b/src/app/api/auth/meta/route.ts index b501e0f..e8305f2 100644 --- a/src/app/api/auth/meta/route.ts +++ b/src/app/api/auth/meta/route.ts @@ -2,10 +2,11 @@ // OAuth — Redirect to Meta Login // ============================================ -import { NextResponse } from 'next/server'; +import { NextRequest, NextResponse } from 'next/server'; import { getSettings } from '@/lib/settings'; +import { getRequestBaseUrl } from '@/lib/url'; -export async function GET() { +export async function GET(request: NextRequest) { const settings = getSettings(); const appId = @@ -14,7 +15,7 @@ export async function GET() { const redirectUri = process.env.META_REDIRECT_URI || - 'https://admanager.seureview.com.br/api/auth/callback'; + new URL('/api/auth/callback', getRequestBaseUrl(request)).toString(); if (!appId || appId === 'your_meta_app_id') { return NextResponse.json( diff --git a/src/lib/url.ts b/src/lib/url.ts new file mode 100644 index 0000000..f9761fd --- /dev/null +++ b/src/lib/url.ts @@ -0,0 +1,25 @@ +import { NextRequest } from 'next/server'; + +/** + * Gets the correct client-facing base URL (protocol + host) of the request. + * Handles reverse proxies by checking standard headers like X-Forwarded-Host and X-Forwarded-Proto. + */ +export function getRequestBaseUrl(request: NextRequest): URL { + const forwardedHost = request.headers.get('x-forwarded-host'); + const forwardedProto = request.headers.get('x-forwarded-proto') || 'https'; + + if (forwardedHost) { + const host = forwardedHost.split(',')[0].trim(); + const proto = forwardedProto.split(',')[0].trim(); + return new URL(`${proto}://${host}`); + } + + const hostHeader = request.headers.get('host'); + if (hostHeader) { + // Standard local dev or direct connection + const proto = request.headers.get('x-forwarded-proto') || 'http'; + return new URL(`${proto}://${hostHeader}`); + } + + return new URL(request.url); +} diff --git a/src/middleware.ts b/src/middleware.ts index a47b091..fec8dc9 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -3,6 +3,7 @@ // ============================================ import { NextRequest, NextResponse } from 'next/server'; +import { getRequestBaseUrl } from '@/lib/url'; export function middleware(request: NextRequest) { const { pathname } = request.nextUrl; @@ -20,7 +21,8 @@ export function middleware(request: NextRequest) { if (process.env.NODE_ENV === 'development') { return NextResponse.next(); } - return NextResponse.redirect(new URL('/login', request.url)); + const baseUrl = getRequestBaseUrl(request); + return NextResponse.redirect(new URL('/login', baseUrl)); } return NextResponse.next();