Setup Remix Root for Auth and CSRF

githubroot.tsx

In root.tsx we setup our actions and loaders

import {
json,
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
LoaderFunction,
useLoaderData,
ActionFunction,
redirect,
Form,
} from "remix";
import {
AuthenticityTokenInput,
AuthenticityTokenProvider,
createAuthenticityToken,
useShouldHydrate,
} from "remix-utils";
import { commitSession } from "~/utils/sessions.server";
import { getSessionData } from "./utils/auth.server";
interface LoaderData {
csrf?: string;
isLoggedIn: boolean;
}
// Setup CSRF token only if they are heading to the login page.
// Usually we would assign a CSRF token to everyone but
// with Firebase Hosting caching is tied to the cookie.
// If someone isn't logged in we want them to hit the public cache
// https://firebase.google.com/docs/hosting/manage-cache
export const action: ActionFunction = async ({ request }) => {
let { session } = await getSessionData(request);
// Add CSRF token to session
createAuthenticityToken(session);
return redirect("/login", {
headers: { "Set-Cookie": await commitSession(session) },
});
};
export const loader: LoaderFunction = async ({ request }) => {
const { csrf, idToken } = await getSessionData(request);
return json<LoaderData>({ csrf, isLoggedIn: !!idToken });
};

Our root App component is the standard Remix one except we conditionally load Javascript and add a navigation bar with login/logout. Throughout the app we make heavy use of the remix-utils npm library.

We use forms to POST to our action endpoints.

export default function App() {
// Control if page loads JS https://github.com/sergiodxa/remix-utils#useshouldhydrate
const shouldHydrate = useShouldHydrate();
const fetcher = useFetcher();
const { csrf, isLoggedIn } = useLoaderData<LoaderData>();
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="robots" content="noindex" />
<Meta />
<Links />
</head>
<body
style={{
margin: 0,
}}
>
<AuthenticityTokenProvider token={csrf || ""}>
<nav
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<a href="/">Home</a>
{/* We use fetcher.Form instead of Form because we dont want navigation events */}
{isLoggedIn ? (
<fetcher.Form action="/logout" method="post" replace>
<AuthenticityTokenInput />
<button type="submit">Logout</button>
</fetcher.Form>
) : (
<fetcher.Form action="/" method="post" replace>
<button type="submit">Login</button>
</fetcher.Form>
)}
</nav>
<Outlet />
</AuthenticityTokenProvider>
<ScrollRestoration />
{shouldHydrate && <Scripts />}
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
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
© 2024 Hasura Inc. All rights reserved
Github
Titter
Discord
Facebook
Instagram
Youtube
Linkedin
graphql-handbook