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
}
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[] {
const anyHeaders = res.headers as any
if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie()
const single = res.headers.get('set-cookie')
return single ? [single] : []
return single ? splitSetCookieHeader(single) : []
}
function parseRefreshCookie(setCookie: string) {
// refreshToken=VALUE; Max-Age=...; Expires=...; SameSite=...; ...
const m = setCookie.match(/^refreshToken=([^;]*)/i)
if (!m) return null
const value = m[1] ?? ''
@ -52,7 +69,23 @@ export async function POST(req: NextRequest) {
const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c))
if (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, {
domain: getSharedCookieDomain(req),
path: '/',

View File

@ -28,6 +28,12 @@ export async function POST(req: NextRequest) {
}
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', '', {
domain: getSharedCookieDomain(req),
path: '/',
@ -36,5 +42,6 @@ export async function POST(req: NextRequest) {
sameSite: 'lax',
maxAge: 0,
})
return out
}

View File

@ -7,11 +7,30 @@ function getSharedCookieDomain(req: NextRequest): string | 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[] {
const anyHeaders = res.headers as any
if (typeof anyHeaders.getSetCookie === 'function') return anyHeaders.getSetCookie()
const single = res.headers.get('set-cookie')
return single ? [single] : []
return single ? splitSetCookieHeader(single) : []
}
function parseRefreshCookie(setCookie: string) {
@ -35,11 +54,24 @@ export async function POST(req: NextRequest) {
const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL
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`, {
method: 'POST',
headers: { cookie },
headers,
cache: 'no-store',
})
@ -50,7 +82,24 @@ export async function POST(req: NextRequest) {
const refreshSetCookie = setCookies.find((c) => /^refreshToken=/i.test(c))
if (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, {
domain: getSharedCookieDomain(req),
path: '/',