fix: cookie stuff

This commit is contained in:
DeathKaioken 2026-01-18 21:48:21 +01:00
parent f10772084d
commit 329a71931b
3 changed files with 96 additions and 7 deletions

View File

@ -7,15 +7,32 @@ function getSharedCookieDomain(req: NextRequest): string | undefined {
return host.endsWith('profit-planet.partners') ? '.profit-planet.partners' : undefined return host.endsWith('profit-planet.partners') ? '.profit-planet.partners' : undefined
} }
function splitSetCookieHeader(header: string): string[] {
const parts: string[] = []
let start = 0
let inExpires = false
for (let i = 0; i < header.length; i++) {
const lower = header.slice(i, i + 8).toLowerCase()
if (lower === 'expires=') inExpires = true
if (inExpires && header[i] === ';') inExpires = false
if (!inExpires && header[i] === ',') {
parts.push(header.slice(start, i).trim())
start = i + 1
}
}
const last = header.slice(start).trim()
if (last) parts.push(last)
return parts
}
function readSetCookies(res: Response): string[] { function readSetCookies(res: Response): string[] {
const anyHeaders = res.headers as any const anyHeaders = res.headers as any
if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie() if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie()
const single = res.headers.get('set-cookie') const single = res.headers.get('set-cookie')
return single ? [single] : [] return single ? splitSetCookieHeader(single) : []
} }
function parseRefreshCookie(setCookie: string) { function parseRefreshCookie(setCookie: string) {
// refreshToken=VALUE; Max-Age=...; Expires=...; SameSite=...; ...
const m = setCookie.match(/^refreshToken=([^;]*)/i) const m = setCookie.match(/^refreshToken=([^;]*)/i)
if (!m) return null if (!m) return null
const value = m[1] ?? '' const value = m[1] ?? ''
@ -52,7 +69,23 @@ export async function POST(req: NextRequest) {
const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c)) const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c))
if (refreshSetCookie) { if (refreshSetCookie) {
const parsed = parseRefreshCookie(refreshSetCookie) const parsed = parseRefreshCookie(refreshSetCookie)
if (parsed) { if (parsed?.value) {
// Clear host-only duplicates first.
out.cookies.set('refreshToken', '', {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 0,
})
out.cookies.set('__Secure-refreshToken', '', {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 0,
})
out.cookies.set('refreshToken', parsed.value, { out.cookies.set('refreshToken', parsed.value, {
domain: getSharedCookieDomain(req), domain: getSharedCookieDomain(req),
path: '/', path: '/',

View File

@ -28,6 +28,12 @@ export async function POST(req: NextRequest) {
} }
const out = NextResponse.json(data, { status }) const out = NextResponse.json(data, { status })
// Clear host-only variants
out.cookies.set('refreshToken', '', { path: '/', httpOnly: true, secure: true, sameSite: 'lax', maxAge: 0 })
out.cookies.set('__Secure-refreshToken', '', { path: '/', httpOnly: true, secure: true, sameSite: 'lax', maxAge: 0 })
// Clear shared-domain variant
out.cookies.set('refreshToken', '', { out.cookies.set('refreshToken', '', {
domain: getSharedCookieDomain(req), domain: getSharedCookieDomain(req),
path: '/', path: '/',
@ -36,5 +42,6 @@ export async function POST(req: NextRequest) {
sameSite: 'lax', sameSite: 'lax',
maxAge: 0, maxAge: 0,
}) })
return out return out
} }

View File

@ -7,11 +7,30 @@ function getSharedCookieDomain(req: NextRequest): string | undefined {
return host.endsWith('profit-planet.partners') ? '.profit-planet.partners' : undefined return host.endsWith('profit-planet.partners') ? '.profit-planet.partners' : undefined
} }
function splitSetCookieHeader(header: string): string[] {
// Handles "Expires=Wed, 21 Oct ..." commas.
const parts: string[] = []
let start = 0
let inExpires = false
for (let i = 0; i < header.length; i++) {
const lower = header.slice(i, i + 8).toLowerCase()
if (lower === 'expires=') inExpires = true
if (inExpires && header[i] === ';') inExpires = false
if (!inExpires && header[i] === ',') {
parts.push(header.slice(start, i).trim())
start = i + 1
}
}
const last = header.slice(start).trim()
if (last) parts.push(last)
return parts
}
function readSetCookies(res: Response): string[] { function readSetCookies(res: Response): string[] {
const anyHeaders = res.headers as any const anyHeaders = res.headers as any
if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie() if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie()
const single = res.headers.get('set-cookie') const single = res.headers.get('set-cookie')
return single ? [single] : [] return single ? splitSetCookieHeader(single) : []
} }
function parseRefreshCookie(setCookie: string) { function parseRefreshCookie(setCookie: string) {
@ -35,11 +54,24 @@ export async function POST(req: NextRequest) {
const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL
if (!apiBase) return NextResponse.json({ message: 'Missing NEXT_PUBLIC_API_BASE_URL' }, { status: 500 }) if (!apiBase) return NextResponse.json({ message: 'Missing NEXT_PUBLIC_API_BASE_URL' }, { status: 500 })
const cookie = req.headers.get('cookie') ?? '' // Prefer a single parsed cookie value to avoid "refreshToken=old; refreshToken=new" ambiguity.
const rt =
req.cookies.get('refreshToken')?.value ??
req.cookies.get('__Secure-refreshToken')?.value ??
''
const headers: Record<string, string> = {}
if (rt) {
headers.cookie = `refreshToken=${rt}`
} else {
// fallback (best-effort)
const cookie = req.headers.get('cookie') ?? ''
if (cookie) headers.cookie = cookie
}
const apiRes = await fetch(`${apiBase}/api/refresh`, { const apiRes = await fetch(`${apiBase}/api/refresh`, {
method: 'POST', method: 'POST',
headers: { cookie }, headers,
cache: 'no-store', cache: 'no-store',
}) })
@ -50,7 +82,24 @@ export async function POST(req: NextRequest) {
const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c)) const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c))
if (refreshSetCookie) { if (refreshSetCookie) {
const parsed = parseRefreshCookie(refreshSetCookie) const parsed = parseRefreshCookie(refreshSetCookie)
if (parsed) { if (parsed?.value) {
// Clear any host-only variants on the frontend host to prevent duplicates.
out.cookies.set('refreshToken', '', {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 0,
})
out.cookies.set('__Secure-refreshToken', '', {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 0,
})
// Set the shared-domain cookie used across subdomains.
out.cookies.set('refreshToken', parsed.value, { out.cookies.set('refreshToken', parsed.value, {
domain: getSharedCookieDomain(req), domain: getSharedCookieDomain(req),
path: '/', path: '/',