fix: derive redirect and origin check from Host header

req.url resolves to the internal hostname in Docker standalone mode.
Read the Host header directly so redirects and CSRF origin checks use
whatever host the browser actually used (IP, hostname, or domain).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jerick
2026-04-20 16:44:11 -04:00
parent 8b0fba5014
commit 2e264014b6

View File

@@ -33,10 +33,22 @@ function isRateLimited(ip: string): boolean {
function hasValidOrigin(req: NextRequest): boolean {
const origin = req.headers.get('origin')
if (!origin) return true // non-browser (curl, server-to-server)
const expected = process.env.NEXTAUTH_URL
? new URL(process.env.NEXTAUTH_URL).origin
: req.nextUrl.origin
return origin === expected
// Accept the host the browser actually used to reach this server.
const host = req.headers.get('x-forwarded-host') ?? req.headers.get('host')
const proto = req.headers.get('x-forwarded-proto') ?? 'http'
const requestOrigin = host ? `${proto}://${host}` : null
if (requestOrigin && origin === requestOrigin) return true
// Also accept the statically configured NEXTAUTH_URL origin (reverse-proxy setups).
if (process.env.NEXTAUTH_URL && origin === new URL(process.env.NEXTAUTH_URL).origin) return true
return false
}
// Build an absolute URL using the Host header the browser sent, not the
// internal hostname Next.js resolves to inside Docker.
function siteUrl(req: NextRequest, path: string): URL {
const host = req.headers.get('x-forwarded-host') ?? req.headers.get('host') ?? 'localhost:3000'
const proto = req.headers.get('x-forwarded-proto') ?? 'http'
return new URL(path, `${proto}://${host}`)
}
export default auth((req) => {
@@ -72,13 +84,13 @@ export default auth((req) => {
if (pathname.startsWith('/api/')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const loginUrl = new URL('/login', req.url)
const loginUrl = siteUrl(req, '/login')
loginUrl.searchParams.set('callbackUrl', pathname)
return NextResponse.redirect(loginUrl)
}
if (pathname === '/login') {
return NextResponse.redirect(new URL('/dashboard', req.url))
return NextResponse.redirect(siteUrl(req, '/dashboard'))
}
return NextResponse.next()