From b204569484fdbad88665b2d73292603444a08623 Mon Sep 17 00:00:00 2001 From: Marshall Polaris Date: Fri, 5 Aug 2022 19:54:05 -0700 Subject: [PATCH] Make server auth code maximally robust --- web/lib/firebase/auth.ts | 44 ++++++++++++++++++++++++--------- web/lib/firebase/server-auth.ts | 19 ++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/web/lib/firebase/auth.ts b/web/lib/firebase/auth.ts index bb4cbcc4..89277abd 100644 --- a/web/lib/firebase/auth.ts +++ b/web/lib/firebase/auth.ts @@ -16,9 +16,34 @@ const CUSTOM_COOKIE_NAME = getAuthCookieName('custom') const ONE_HOUR_SECS = 60 * 60 const TEN_YEARS_SECS = 60 * 60 * 24 * 365 * 10 -export const getAuthCookies = (request?: IncomingMessage) => { - const data = request != null ? request.headers.cookie ?? '' : document.cookie - const cookies = getCookies(data) +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 getAuthCookies = (req?: IncomingMessage) => { + const cookies = getCookies(getCookieDataIsomorphic(req)) return { idToken: cookies[ID_COOKIE_NAME] as string | undefined, refreshToken: cookies[REFRESH_COOKIE_NAME] as string | undefined, @@ -30,7 +55,7 @@ export const setAuthCookies = ( idToken?: string, refreshToken?: string, customToken?: string, - response?: ServerResponse + res?: ServerResponse ) => { const idMaxAge = idToken != null ? ONE_HOUR_SECS : 0 const idCookie = setCookie(ID_COOKIE_NAME, idToken ?? '', [ @@ -53,13 +78,8 @@ export const setAuthCookies = ( ['samesite', 'lax'], ['secure'], ]) - if (response != null) { - response.setHeader('Set-Cookie', [idCookie, refreshCookie, customCookie]) - } else { - document.cookie = idCookie - document.cookie = refreshCookie - document.cookie = customCookie - } + setCookieDataIsomorphic([idCookie, refreshCookie, customCookie], res) } -export const deleteAuthCookies = () => setAuthCookies() +export const deleteAuthCookies = (res?: ServerResponse) => + setAuthCookies(undefined, undefined, undefined, res) diff --git a/web/lib/firebase/server-auth.ts b/web/lib/firebase/server-auth.ts index 1dd74c9c..ccfb379a 100644 --- a/web/lib/firebase/server-auth.ts +++ b/web/lib/firebase/server-auth.ts @@ -135,14 +135,19 @@ const authAndRefreshTokens = async (ctx: RequestContext) => { export const authenticateOnServer = async (ctx: RequestContext) => { const tokens = await authAndRefreshTokens(ctx) - if (tokens == null) { - deleteAuthCookies() - return undefined - } else { - const { creds, idToken, refreshToken, customToken } = tokens - setAuthCookies(idToken, refreshToken, customToken, ctx.res) - return creds + const creds = tokens?.creds + try { + if (tokens == null) { + deleteAuthCookies(ctx.res) + } else { + const { idToken, refreshToken, customToken } = tokens + setAuthCookies(idToken, refreshToken, customToken, ctx.res) + } + } catch (e) { + // definitely not supposed to happen, but let's be maximally robust + console.error(e) } + return creds } // note that we might want to define these types more generically if we want better