Skip to content

Instantly share code, notes, and snippets.

@jamesdaniels
Last active November 4, 2024 19:35
Show Gist options
  • Save jamesdaniels/8a9e7b65eb4f58f9e4f4ccdbc3f758d2 to your computer and use it in GitHub Desktop.
Save jamesdaniels/8a9e7b65eb4f58f9e4f4ccdbc3f758d2 to your computer and use it in GitHub Desktop.
'use client'
import { useEffect, useState } from 'react';
import { onIdTokenChanged, beforeAuthStateChanged } from 'firebase/auth';
import { COOKIE_NAME } from "@/firebase/constants";
import { auth } from '@/firebase/client';
import { set as setCookie, remove as removeCookie, get as getCookie } from "@/cookies";
export default function FirebaseClientCookieProvider({ serverUid }: { serverUid?: string }) {
const [authStateReady, setAuthStateReady] = useState(false);
useEffect(() => {
auth.authStateReady().then(() => setAuthStateReady(true));
}, []);
useEffect(() => {
if (!authStateReady) return;
// for refreshing the router we care about the uid from the last render pass, in this
// case it will have been from the server
const unsubscribe = onIdTokenChanged(auth, async (user) => {
const userChanged = user?.uid !== serverUid;
if (userChanged) unsubscribe();
// the cookie is set more often then it needs to be here but this is a cheap
// operation IMO
if (user) {
const idToken = await user.getIdToken();
await setCookie(COOKIE_NAME, idToken);
} else {
await removeCookie(COOKIE_NAME);
}
if (userChanged) window.location.reload();
});
return unsubscribe;
}, [authStateReady, serverUid]);
useEffect(() => {
// Using beforeAuthStateChanged will ensure that the Cookie is set before onAuthStateChanged
// fires, if the Cookie is not able to be set or deleted—the user will not be logged in/out
let priorCookieValue: string|undefined;
return beforeAuthStateChanged(auth, async (user) => {
priorCookieValue = await getCookie(COOKIE_NAME);
const idToken = await user?.getIdToken();
if (idToken) {
return setCookie(COOKIE_NAME, idToken);
} else {
return removeCookie(COOKIE_NAME);
}
}, () => {
// If another beforeAuthStateChanged rejects, revert the cookie (best-effort)
if (priorCookieValue) {
setCookie(COOKIE_NAME, priorCookieValue);
} else {
removeCookie(COOKIE_NAME);
}
});
}, []);
return (
<></>
)
}
import "server-only";
import { FirebaseServerApp, initializeServerApp } from 'firebase/app';
import { getAuth } from "firebase/auth";
import { FIREBASE_OPTIONS, COOKIE_NAME } from "./constants";
import { cookies } from "next/headers";
async function getServerApp() {
const authIdToken = (await cookies()).get(COOKIE_NAME)?.value;
return initializeServerApp(FIREBASE_OPTIONS, { authIdToken });
}
export async function getServerAuth(app?: FirebaseServerApp) {
const serverApp = app || await getServerApp();
const serverAuth = getAuth(serverApp);
await serverAuth.authStateReady();
return serverAuth;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment