This course is no longer maintained and may be out-of-date. While it remains available for reference, its content may not reflect the latest updates, best practices, or supported features.

Create login component

githublogin.tsx

We plan to have the user login via the client-side Firebase UI component, then send up the JWT to be turned into a session cookie.

import { useEffect } from "react";
import { ActionFunction, redirect, useSubmit } from "remix";
import {
ClientOnly,
createAuthenticityToken,
unauthorized,
useAuthenticityToken,
useHydrated,
} from "remix-utils";
import { commitSession } from "~/utils/sessions.server";
import { admin } from "~/utils/firebase.server";
import { getSessionData } from "~/utils/auth.server";
import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth";
import firebase from "firebase/compat/app";
import "firebase/compat/auth";
// We need Javascript client side to run the Firebase Login component
export const handle = { hydrate: true };
export const action: ActionFunction = async ({ request }) => {
// Get the session and verify the CSRF token
const { session } = await getSessionData(request, true);
const form = await request.formData();
const idToken = form.get("idToken") as string;
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
try {
const token = await admin.auth().verifyIdToken(idToken);
if (new Date().getTime() / 1000 - token.auth_time < 5 * 60) {
// Create session cookie and set it.
const cookie = await admin
.auth()
.createSessionCookie(idToken, { expiresIn });
session.set("idToken", cookie);
// Create a new CSRF token to avoid session fixation attacks
// https://owasp.org/www-community/attacks/Session_fixation
createAuthenticityToken(session);
return redirect("/", {
headers: { "Set-Cookie": await commitSession(session) },
});
}
// If the JWT is too old we reject it
admin.auth().revokeRefreshTokens(token.sub);
return new Response("Recent sign in required!", {
status: 401,
});
} catch (error) {
throw unauthorized("Token invalid");
}
};

With the login component we make sure we are in the browser, not the server, and initialize Firebase using the config we got when creating the project. Once a user logs in we POST to our server with the JWT token, turn it into a session cookie, and redirect to the homepage.

For homework, implement the Firebase config using Remix browser environment variables.

export default function Login() {
const submit = useSubmit();
const csrf = useAuthenticityToken();
// Check if we are in the browser or server
const hydated = useHydrated();
useEffect(() => {
if (!firebase.apps.length)
firebase.initializeApp(<Firebase config from create project screen>);
// Our auth is persisted in our cookie
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
}, []);
return (
<ClientOnly>
{hydated && (
<StyledFirebaseAuth
uiConfig={{
// Popup signin flow rather than redirect flow.
signInFlow: "redirect",
callbacks: {
// On sign in we POST our server with the JWT token
signInSuccessWithAuthResult: (
authResult: firebase.auth.UserCredential
) => {
authResult.user?.getIdToken().then((idToken) => {
const formData = new FormData();
formData.append("idToken", idToken);
formData.append("csrf", csrf);
submit(formData, {
method: "post",
action: "/login",
// Don't create entry on browser history stack
replace: true,
});
});
return false;
},
},
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
requireDisplayName: false,
},
],
}}
firebaseAuth={firebase.auth()}
></StyledFirebaseAuth>
)}
</ClientOnly>
);
}
Did you find this page helpful?
Start with GraphQL on Hasura for Free
  • ArrowBuild apps and APIs 10x faster
  • ArrowBuilt-in authorization and caching
  • Arrow8x more performant than hand-rolled APIs
Promo
footer illustration
Brand logo
© 2025 Hasura Inc. All rights reserved
Github
Titter
Discord
Facebook
Instagram
Youtube
Linkedin
graphql-handbook