firebase-frameworks
Version:
Experimental addon to the Firebase CLI to add web framework support
94 lines (93 loc) • 3.43 kB
JavaScript
import { initializeApp as initializeAdminApp, getApps } from "firebase-admin/app";
import { getAuth as getAdminAuth } from "firebase-admin/auth";
import { initializeApp, deleteApp } from "firebase/app";
import { getAuth, signInWithCustomToken } from "firebase/auth";
import cookie from "cookie";
import LRU from "lru-cache";
import { COOKIE_MAX_AGE, ID_TOKEN_MAX_AGE, LRU_MAX_INSTANCES, LRU_TTL } from "./constants.js";
const ADMIN_APP_NAME = "firebase-frameworks";
const adminApp = getApps().find((it) => it.name === ADMIN_APP_NAME) ||
initializeAdminApp(undefined, ADMIN_APP_NAME);
const adminAuth = getAdminAuth(adminApp);
const firebaseAppsLRU = new LRU({
max: LRU_MAX_INSTANCES,
ttl: LRU_TTL,
allowStale: true,
updateAgeOnGet: true,
dispose: (value) => {
deleteApp(value);
},
});
const mintCookie = async (req, res) => {
const idToken = req.header("Authorization")?.split("Bearer ")?.[1];
const verifiedIdToken = idToken ? await adminAuth.verifyIdToken(idToken) : null;
if (verifiedIdToken) {
if (new Date().getTime() / 1000 - verifiedIdToken.iat > ID_TOKEN_MAX_AGE) {
res.status(301).end();
}
else {
const cookie = await adminAuth
.createSessionCookie(idToken, { expiresIn: COOKIE_MAX_AGE })
.catch((e) => {
console.error(e.message);
});
if (cookie) {
const options = { maxAge: COOKIE_MAX_AGE, httpOnly: true, secure: true };
res.cookie("__session", cookie, options).status(201).end();
}
else {
res.status(401).end();
}
}
}
else {
res.status(204).clearCookie("__session").end();
}
};
const handleAuth = async (req, res) => {
const cookies = cookie.parse(req.headers.cookie || "");
const { __session } = cookies;
if (!__session)
return;
const decodedIdToken = await adminAuth
.verifySessionCookie(__session)
.catch((e) => console.error(e.message));
if (!decodedIdToken)
return;
const { uid } = decodedIdToken;
let app = firebaseAppsLRU.get(uid);
if (!app) {
const isRevoked = !(await adminAuth
.verifySessionCookie(__session, true)
.catch((e) => console.error(e.message)));
if (isRevoked)
return;
const random = Math.random().toString(36).split(".")[1];
const appName = `authenticated-context:${uid}:${random}`;
// Force JS SDK autoinit with the undefined
// eslint-disable-next-line @typescript-eslint/no-explicit-any
app = initializeApp(undefined, appName);
firebaseAppsLRU.set(uid, app);
}
const auth = getAuth(app);
if (auth.currentUser?.uid !== uid) {
// TODO(jamesdaniels) get custom claims
const customToken = await adminAuth
.createCustomToken(uid)
.catch((e) => console.error(e.message));
if (!customToken)
return;
await signInWithCustomToken(auth, customToken);
}
res.locals.firebaseApp = app;
res.locals.currentUser = auth.currentUser;
};
export const handleFactory = (frameworkHandle) => async (req, res) => {
if (req.url === "/__session") {
await mintCookie(req, res);
}
else {
await handleAuth(req, res);
frameworkHandle(req, res);
}
};