Implement server-side authentication cookie reading logic
This commit is contained in:
parent
1576c2f12a
commit
cedc1c3be5
63
web/lib/firebase/server-auth.ts
Normal file
63
web/lib/firebase/server-auth.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import * as admin from 'firebase-admin'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import { IncomingMessage, ServerResponse } from 'http'
|
||||||
|
import { FIREBASE_CONFIG, PROJECT_ID } from 'common/envs/constants'
|
||||||
|
import { getAuthCookies, setAuthCookies } from './auth'
|
||||||
|
|
||||||
|
const ensureApp = async () => {
|
||||||
|
// Note: firebase-admin can only be imported from a server context,
|
||||||
|
// because it relies on Node standard library dependencies.
|
||||||
|
if (admin.apps.length === 0) {
|
||||||
|
// never initialize twice
|
||||||
|
return admin.initializeApp({ projectId: PROJECT_ID })
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
return admin.apps[0]!
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestFirebaseIdToken = async (refreshToken: string) => {
|
||||||
|
// See https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token
|
||||||
|
const refreshUrl = new URL('https://securetoken.googleapis.com/v1/token')
|
||||||
|
refreshUrl.searchParams.append('key', FIREBASE_CONFIG.apiKey)
|
||||||
|
const result = await fetch(refreshUrl.toString(), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if (!result.ok) {
|
||||||
|
throw new Error(`Could not refresh ID token: ${await result.text()}`)
|
||||||
|
}
|
||||||
|
return (await result.json()) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestContext = {
|
||||||
|
req: IncomingMessage
|
||||||
|
res: ServerResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerAuthenticatedUid = async (ctx: RequestContext) => {
|
||||||
|
const app = await ensureApp()
|
||||||
|
const auth = app.auth()
|
||||||
|
const { idToken, refreshToken } = getAuthCookies(ctx.req)
|
||||||
|
|
||||||
|
// If we have a valid ID token, verify the user immediately with no network trips.
|
||||||
|
// If the ID token doesn't verify, we'll have to refresh it to see who they are.
|
||||||
|
// If they don't have any tokens, then we have no idea who they are.
|
||||||
|
if (idToken != null) {
|
||||||
|
try {
|
||||||
|
return (await auth.verifyIdToken(idToken))?.uid
|
||||||
|
} catch (e) {
|
||||||
|
if (refreshToken != null) {
|
||||||
|
const resp = await requestFirebaseIdToken(refreshToken)
|
||||||
|
setAuthCookies(resp.id_token, resp.refresh_token, ctx.res)
|
||||||
|
return (await auth.verifyIdToken(resp.id_token))?.uid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user