Setting up Server Side Auth with Supabase and NextJS
Introduction
When it comes to choosing your stack for your next SaaS project, NextJS is a must choice in the react ecosystem, NextJS also make a good match with supabase.com/ an Open source firebase alternative. In this post, I will show you how to integrate the supabase auth logic in NextJS in an idiomatic way.
Setting up a custom App
irst, we will need to create the pages/_app.tsx
.
This will allow us to create reusable logic among our App.
In this React Component page, we will use the SDK provided by supabase to handle authentification.
// pages/_app.tsx
import { supabase } from "../../utils/supabase";
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
supabase.auth.onAuthStateChange((event, session) => {
fetch("/api/auth", {
method: "POST",
headers: new Headers({ "Content-Type": "application/json" }),
credentials: "same-origin",
body: JSON.stringify({ event, session }),
});
return (
<Component {...pageProps} />
)
}
This piece of code will allow performing a POST HTTP request to our future API route pages/api/auth.ts
with metadata event
and session
an object that contains key pieces of information (auth cookie) about the user who is logged or not.
We are listening to any changes that could happen related to authentication (sign in, sign out, etc…) thanks to the supabase SDK auth. Once an event happens, we will immediately tell the server-side that the auth state has changed, then the server-side will update his state accordingly.
The good news as said earlier is that supabase is open source, which means that you can check what’s going on under the hood. For our use case, the logic happens here.
Setting up API Route Auth
Once the client is ready to send the event
and session
events, we need to create the function server-side that will receive that payload to interpret it.
// pages/pages/api/auth.ts
import { NextApiRequest, NextApiResponse } from "next";
import { supabase } from "../../utils/supabase";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// Set the auth cookie.
supabase.auth.api.setAuthCookie(req, res);
}
We are using again the supabase
object that gives access to the set of API related to auth. setAuthCookie
is using the under the hood the Set-Cookie HTTP header that allows the user agent to store a cookie that will be used in a near future.
We are already almost done!
Protecting routes
We now have the ability to protect our route thanks to the getServerSideProps function of NextJS.
// pages/protectedRoute.tsx
import { GetServerSideProps } from "next";
import { supabase } from "../../utils/supabase";
const Protected = ({ user }: IProps) => {
return <div>JSON.stringify(user)</div>
}
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
// Get our logged user
const { user } = await supabase.auth.api.getUserByCookie(req);
// Check if the user is logged
if (user === null) {
// Redirect if no logged in
return { props: {}, redirect: { destination: "/auth/login" } };
}
// If logged return the user
return { props: { user } };
};
export default Protected;
The function is really straightforward, it will look in the cookie of the request if there is any auth cookie available to consume.
If an auth cookie is present, the function will fetch the user associated with the cookie from the backend and return his metadata.
On the other hand, if no auth cookie is detected, the API will not be able to fetch that user and then return null
value, to indicate it.
Then, once we have our result, we will use the special API that getServerSideProps is providing, by either redirecting the user to the login page before he could reach the client rendered or making him access to the page asked with the desired props.
Then, that getServerSideProps function should be replicated to every page your application logic ask for it. You can even do the inverse, for example when an already authenticated user navigates to the login page. You can redirect him to your main protected page instead.
Conclusion
It exists either the server-side way of doing it or the client-side way. It’s generally a better approach to do it server-side way whenever possible to avoid a few edge cases where the application could behave wrongly.
Everything on the client side could be reached, so it means we should never trust the client side. On the other hand, on the server-side, it is way more secure and hard to tweak.
I hope you will find it useful!