import { PROJECT_ID } from 'common/envs/constants' import { setCookie, getCookies } from '../util/cookie' import { IncomingMessage, ServerResponse } from 'http' const ONE_HOUR_SECS = 60 * 60 const TEN_YEARS_SECS = 60 * 60 * 24 * 365 * 10 const TOKEN_KINDS = ['refresh', 'id', 'custom'] as const const TOKEN_AGES = { id: ONE_HOUR_SECS, refresh: TEN_YEARS_SECS, custom: ONE_HOUR_SECS, } as const export type TokenKind = typeof TOKEN_KINDS[number] const getAuthCookieName = (kind: TokenKind) => { const suffix = `${PROJECT_ID}_${kind}`.toUpperCase().replace(/-/g, '_') return `FIREBASE_TOKEN_${suffix}` } const COOKIE_NAMES = Object.fromEntries( TOKEN_KINDS.map((k) => [k, getAuthCookieName(k)]) ) as Record const getCookieDataIsomorphic = (req?: IncomingMessage) => { if (req != null) { return req.headers.cookie ?? '' } else if (document != null) { return document.cookie } else { throw new Error( 'Neither request nor document is available; no way to get cookies.' ) } } const setCookieDataIsomorphic = (cookies: string[], res?: ServerResponse) => { if (res != null) { res.setHeader('Set-Cookie', cookies) } else if (document != null) { for (const ck of cookies) { document.cookie = ck } } else { throw new Error( 'Neither response nor document is available; no way to set cookies.' ) } } export const getTokensFromCookies = (req?: IncomingMessage) => { const cookies = getCookies(getCookieDataIsomorphic(req)) return Object.fromEntries( TOKEN_KINDS.map((k) => [k, cookies[COOKIE_NAMES[k]]]) ) as Partial> } export const setTokenCookies = ( cookies: Partial>, res?: ServerResponse ) => { const data = TOKEN_KINDS.filter((k) => k in cookies).map((k) => { const maxAge = cookies[k] ? TOKEN_AGES[k as TokenKind] : 0 return setCookie(COOKIE_NAMES[k], cookies[k] ?? '', [ ['path', '/'], ['max-age', maxAge.toString()], ['samesite', 'lax'], ['secure'], ]) }) setCookieDataIsomorphic(data, res) } export const deleteTokenCookies = (res?: ServerResponse) => setTokenCookies({ id: undefined, refresh: undefined, custom: undefined }, res)