import { headers } from 'next/headers' const AUTH_COOKIE_BASE_URL = process.env.APP_PUBLIC_URL || process.env.NEXTAUTH_URL || '' export const SECURE_AUTH_SESSION_COOKIE_NAME = '__Secure-next-auth.session-token' export const PLAIN_AUTH_SESSION_COOKIE_NAME = 'next-auth.session-token' type HeaderAccessor = | Headers | { get?: (name: string) => string | null | undefined [key: string]: string | string[] | undefined | ((name: string) => string | null | undefined) } type RequestLike = { headers?: HeaderAccessor nextUrl?: { protocol?: string } url?: string } function normalizeProtocol(value?: string | null) { if (!value) { return null } const normalized = value.split(',')[0].trim().replace(/:$/, '').toLowerCase() if (normalized === 'https' || normalized === 'http') { return normalized } return null } function getHeaderValue(headersLike: HeaderAccessor | undefined, name: string) { if (!headersLike) { return null } if (typeof headersLike.get === 'function') { return headersLike.get(name) ?? null } const recordHeaders = headersLike as Record const directValue = recordHeaders[name] if (Array.isArray(directValue)) { return directValue[0] ?? null } if (typeof directValue === 'string') { return directValue } const lowerCaseValue = recordHeaders[name.toLowerCase()] if (Array.isArray(lowerCaseValue)) { return lowerCaseValue[0] ?? null } return typeof lowerCaseValue === 'string' ? lowerCaseValue : null } function resolveProtocolFromHeaders(headersLike?: HeaderAccessor) { const forwardedProto = normalizeProtocol(getHeaderValue(headersLike, 'x-forwarded-proto')) if (forwardedProto) { return forwardedProto } const forwardedSsl = getHeaderValue(headersLike, 'x-forwarded-ssl') if (typeof forwardedSsl === 'string' && forwardedSsl.toLowerCase() === 'on') { return 'https' } const forwardedPort = getHeaderValue(headersLike, 'x-forwarded-port') if (forwardedPort === '443') { return 'https' } return null } function resolveProtocolFromUrl(url?: string) { if (!url) { return null } try { return normalizeProtocol(new URL(url).protocol) } catch { return null } } function resolveSecureCookieFromPublicBase(headersLike?: HeaderAccessor, url?: string) { if (!AUTH_COOKIE_BASE_URL.startsWith('https://')) { return null } try { const publicUrl = new URL(AUTH_COOKIE_BASE_URL) const forwardedHost = getHeaderValue(headersLike, 'x-forwarded-host') const host = forwardedHost || getHeaderValue(headersLike, 'host') || (url ? new URL(url).host : '') if (!host) { return null } const normalizedHost = host.split(',')[0].trim().toLowerCase() return normalizedHost === publicUrl.host.toLowerCase() ? true : null } catch { return null } } export function resolveSecureAuthCookieFromRequest(request?: RequestLike) { const secureFromPublicBase = resolveSecureCookieFromPublicBase(request?.headers, request?.url) if (secureFromPublicBase === true) { return true } const protocolFromHeaders = resolveProtocolFromHeaders(request?.headers) if (protocolFromHeaders) { return protocolFromHeaders === 'https' } const protocolFromNextUrl = normalizeProtocol(request?.nextUrl?.protocol) if (protocolFromNextUrl) { return protocolFromNextUrl === 'https' } const protocolFromUrl = resolveProtocolFromUrl(request?.url) if (protocolFromUrl) { return protocolFromUrl === 'https' } return AUTH_COOKIE_BASE_URL.startsWith('https://') } export function resolveSecureAuthCookieForCurrentRequest() { try { return resolveSecureAuthCookieFromRequest({ headers: headers(), }) } catch { return AUTH_COOKIE_BASE_URL.startsWith('https://') } } export function getAuthSessionCookieName(useSecureCookie: boolean) { return useSecureCookie ? SECURE_AUTH_SESSION_COOKIE_NAME : PLAIN_AUTH_SESSION_COOKIE_NAME } export function getAuthSessionCookieCandidates(preferredSecureCookie: boolean) { const preferredName = getAuthSessionCookieName(preferredSecureCookie) const fallbackName = getAuthSessionCookieName(!preferredSecureCookie) return preferredName === fallbackName ? [preferredName] : [preferredName, fallbackName] }