Magic Link Authentication and Route Controls with Supabase and Next.js

Kane W.
6 min readJul 19, 2021

While Supabase is widely known for their real-time database and API layer, one of the things I like about it is the number of easy to set up authentication mechanisms it offers out of the box.

Magic Link

One of my favorites is Magic Link. You’ve probably used magic link in the past. Magic link sends a link to the user via email containing a link to authenticate with the service via a custom URL and access token.

When the user visits the URL, a session is set in their browser storage and the user is redirected back to the app, authenticating the user in the process.

This is becoming a very popular way to authenticate users as they do not have to keep up with another password, it provides a really great user experience.

Next.js

With Next.js, you have the ability to not only protect routes with client-side authorization, but for added security you can do server-side authorization and redirects in getServerSideProps if a cookie has been set and is available in the request context.

This is also where Supabase comes in handy. There is built-in functionality for setting and getting the cookie for the signed in user in SSR and API routes:

Setting the user in an API route

Getting the user in an SSR or API route

Server-side redirects are typically preferred over client-side redirects from an SEO perspective — it’s harder for search engines to understand how client-side redirects should be treated.

You are also able to access the user profile from an API route using the getUserByCookie function, opening up an entirely new set of use cases and functionality.

With Next.js and Supabase you can easily implement a wide variety of applications using this combination of SSG, SSR, and client-side data fetching and user authorization, making the combination (and any framework that offers this combination of capabilities) extremely useful and powerful.

What we’ll be building

In this post, we’ll build out a Next.js app that enables navigation, authentication, authorization, redirects (client and server-side), and a profile view.

The project that we’ll be building is a great starting point for any application that needs to deal with user identity, and is a good way to understand how user identity works and flows throughout all of the different places in a project using a modern hybrid framework like Next.js.

The final code for this project is located here

Building the app

To get started, you first need to create a Supabase account and project.

To do so, head over to Supabase.io and click Start Your Project. Authenticate with GitHub and then create a new project under the organization that is provided to you in your account.

Give the project a Name and Password and click Create new project.

It will take approximately 2 minutes for your project to be created.

Next, open your terminal and create a new Next.js app:

npx create-next-app supabase-next-authcd supabase-next-auth

The only dependency we’ll need is the @supabase/supabase-js package:

npm install @supabase/supabase-js

Configuring the Supabase credentials

Now that the Next.js app is created, it needs a to know about the Supabase project in order to interact with it.

The best way to do this is using environment variables. Next.js allows environment variables to be set by creating a file called .env.local in the root of the project and storing them there.

In order to expose a variable to the browser you have to prefix the variable with NEXT_PUBLIC_.

Create a file called .env.local at the root of the project, and add the following configuration:

NEXT_PUBLIC_SUPABASE_URL=https://app-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-public-api-key

You can find the values of your API URL and API Key in the Supabase dashboard settings:

Creating the Supabase client

Now that the environment variables have been set, we can create a Supabase instance that can be imported whenever we need it.

Create a file named client.js in the root of the project with the following code:

Updating the index page

Next, let’s update pages/index.js to be something more simple than what is provided out of the box. This is just meant to serve as a basic landing page

Creating the sign in screen

Next, let’s create the Sign In screen. This will serve a form input for the user to provide their email address.

When the user submits the form, they will receive a magic link to sign in. This will work for both new as well as existing users!

Create a new file in the pages directory named sign-in.js:

The main thing in this file is this line of code:

By only providing the email address of the user, magic link authentication will happen automatically.

Profile view

Next, let’s create the profile view. Create a new file in the pages directory named profile.js:

To check for the currently signed in user we call supabase.auth.user().

If the user is signed in, we set the user information using the setProfile function set up using the useState hook.

If the user is not signed in, we client-side redirect using the useRouter hook.

API Route

In pages/_app.js we’ll be needing to call a function to set the cookie for retrieval later in the SSR route.

Let’s go ahead and create that API route and function. This will be calling the setAuthCookie API given to us by the Supabase client.

Create a new file named auth.js in the pages/api folder and add the following code:

Nav, auth listener, and setting the session cookie

The largest chunk of code we’ll need to write will be in pages/app.js. Here are the things we need to implement here:

  1. Navigation
  2. A listener to fire when authentication state changes (provided by Supabase)
  3. A function that will set the cookie with the user session

In addition to this, we’ll also need to keep up with the authenticated state of the user. We do this so we can toggle links, showing or hiding certain links based on if the user is or isn’t signed in.

We’ll demonstrate this here by only showing the Sign In link to users who are not signed in, and hiding it when they are.

The last page we need to implement is the route that will demonstrate server-side protection and redirects.

Since we have already implemented setting the cookie, we should now be able to read the cookie on the server if the user is signed in.

Like I mentioned previously, we can do this with the getUserByCookie function.

Create a new file in the pages directory named protected.js and add the following code:

Testing it out

Now the app is built and we can test it out!

To run the app, open your terminal and run the following command:

npm run dev

When the app loads, you should be able to sign up, and sign in using the magic link. Once signed in, you should be able to view the profile page and see your user id as well as your email address.

Setting metadata and attributes

If you want to continue building out the user’s profile, you can do so easily using the update method.

For example, let’s say we wanted to allow the user’s to set their location. We can do so with the following code:

const { user, error } = await supabase.auth.update({ 
data: {
city: "New York"
}
})

Now, when we fetch the user’s data, we should be able to view their metadata:

The final code for this project is located here

--

--

Kane W.

Principle Developer: #Blockchain, #React, #Node, #SQL, #NoSQL, #PHP, #Android, #iOS, #Cryptocurrency, #Python, #Ruby, #Javascript, #CSS, #HTML5