Magic Link Sign in using Next Auth, Prisma, Amazon SES and Supabase
Tags: nextjs, aws, prisma, supabase
Published: 2022-08-09 08:06:00

## Introduction

I've always been curious about how the magic links that all websites are using nowadays work and over the weekend, after watching a few videos, I put together a quick prototype that manages to do so. Let me walk you through it!

## Installing Necessary Libraries and bootstrapping the project

Before we begin, we need to start by bootstrapping a new NextJS App. You can do so by running the command

```
npx create-next-app --ts
```

This will help us to boot upa NextJS App with Typescript auto configured. Now we need to install our dependencies, which will be prisma and next-auth. We can do so by running the commands

```
yarn add prisma next-auth @next-auth/prisma-adapter nodemailer
```

Let's also add a `.env` file which looks like what we have below 

```
DATABASE_URL = 
SMTP_HOST= 
SMTP_PORT = 587 
SMTP_USER = 
SMTP_PASSWORD = 
SMTP_FROM=
NEXTAUTH_SECRET = 
```

## Supabase

### Creating Our Project

We now need to create a quick database with Supabase. If you don't have an account, you can sign up [here](https://supabase.com/) and create a free account. Once you've done so, simply go over to the dashboard and create a new project.

![Screenshot 2022-08-09 at 2 25 36 PM](https://user-images.githubusercontent.com/45760326/183579268-71c83950-282b-4faf-b283-865f1f30c043.png)

Make sure to write down your password so that you don't forget it. You'll need it to connect to your database later on.

### Grabbing our Database URI

We can find our database URI by navigating to **settings -> database -> connection string -> URI**. I've provided some snapshots below to help you find it in your project.

![Screenshot 2022-08-09 at 3 24 08 PM](https://user-images.githubusercontent.com/45760326/183589454-b2cb1641-580a-410e-844a-39fc0dad6cae.png)
![Screenshot 2022-08-09 at 3 24 24 PM](https://user-images.githubusercontent.com/45760326/183589500-af92709b-94e0-4cf9-b6b9-af242ef68050.png)
<img width="1039" alt="Screenshot 2022-08-09 at 3 25 52 PM" src="https://user-images.githubusercontent.com/45760326/183589795-95cd51c1-a7a8-467c-922b-0a1ec4049047.png">

The password will simply be the password that you set up when configuring the project.

## Prisma

### Configuring our schema

> Make sure that you've updated the .env file with the database URI that we grabbed from supabase. This should be named DATABASE_URL in the .env file.

Now that we've got a working database, we can start to setup our prisma schema to work nicely with it. Let's start by running the command 

```
npx prisma init
```

This will create for us a new `schema.prisma` file which we want to override with the code below

```
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}
```

Let's now test that our database is working as we planned by running a migration using the command

```
npx prisma migrate dev
```

This will apply our changes to our database and we can verify this by going back to supabase and verifying that our new tables have been created as we wanted.


![Screenshot 2022-08-09 at 3 32 09 PM](https://user-images.githubusercontent.com/45760326/183591043-972581dc-d19d-4d53-8975-986c27322d7d.png)

## Configuring Amazon Simple Email Service ( SES )

### Creating a Verified Identity

> Amazon SES is a email service provided by Amazon which helps you to send emails using either the AWS SDK or an SMTP Endpoint they provide. **For This article, we will be using their SMTP credentials**.

First, create an AWS account if you haven't. AWS is Amazon's cloud computing wing which provides a variety of different services. You can do so [here](https://aws.amazon.com/). Once you've created a new account, navigate to the SES dashboard and click on **Verified Identities**.

![Screenshot 2022-08-09 at 3 15 48 PM](https://user-images.githubusercontent.com/45760326/183587802-dde36f44-8dbe-4fe7-b6b5-e3b671df5e05.png)

![Screenshot 2022-08-09 at 3 16 21 PM](https://user-images.githubusercontent.com/45760326/183587906-38c3ba2d-c9f3-4e1e-bdd8-3ad8008d5099.png)

If you own a domain, you'll need to wait a while before being able to start sending emails out with your registered domain.

## Creating an IAM User with SMTP credentials

Now that we've got ourselves a working verified domain, we now need to configure a valid IAM user with SMTP credentials. In order to do so, we can simply navigate to the **Account Dashboard** screen in the SES page

![Screenshot 2022-08-09 at 3 19 02 PM](https://user-images.githubusercontent.com/45760326/183588395-74a7bcda-0e3c-414e-827e-7a516bcc20cf.png)

Now simply create a new user with the wizard. Name it anything you like but note down the username and password that Amazon provides you with. You'll need it to configure our NextAuth Login Flow.

With this, we've got ourselves all the credentails we need.

## NextAuth

### Configuring our Provider

Let's now start to set up our NextAuth in our project. Let's first open our `_app.tsx`file and add in the NextAuth Providers

```js
import "../styles/globals.css";
import { SessionProvider } from "next-auth/react";

export default function App({
  //@ts-ignore
  Component,
  //@ts-ignore
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}
```

As you can seem we now have a new Session Provider which can help us to utilise NextAuth in our application. This helps track the individual sessions which are running in our app and in turn utilise useful functionality such as the `useSession` hook in our app.

## Updating Next Auth API Route

We now need to create a new route in the NextJS Project that we have created. This will have the route of `pages/api/auth/[...nextauth].js` and will look like what we have below. 

```js
import NextAuth from "next-auth";
import EmailProvider from "next-auth/providers/email";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default NextAuth({
  // Configure one or more authentication providers
  providers: [
    EmailProvider({
      server: `smtp://${process.env.SMTP_USER}:${process.env.SMTP_PASSWORD}@${process.env.SMTP_HOST}:${process.env.SMTP_PORT}`,
      from: process.env.SMTP_FROM,
    }),
    // ...add more providers here
  ],
  adapter: PrismaAdapter(prisma),
});

```

We now need to make sure that our .env file has the relevant values

- **SMTP_USER** : This is the username generated by AWS when you created your new IAM user
- **SMTP_PASSWORD**: This is the password generated by AWS when we created the new IAM user with SMTP priviledges.
- **SMTP_HOST**: This will depend on what region you want to send the emails in. For mine it is `email-smtp.ap-southeast-1.amazonaws.com` and you can find the specific SMTP endpoint to use [here](https://docs.aws.amazon.com/general/latest/gr/ses.html)
- **SMTP_PORT**: 587
- **SMTP_FROM**: This is the value that will appear in the **FROM** field in your email. Note that you can only add enails that have been verified by SES in this step.

## Configuring index.tsx

We've now got everything set up. Let's test to see if it's working, to do so, we can use our NextJS application to test. Let's scaffold a quick application which uses the `useSession` hook in order to see if our magic link authentication works

```
import type { NextPage } from "next";
import { signIn, signOut, useSession } from "next-auth/react";

const Home: NextPage = () => {
  const { data: session } = useSession();

  if (session) {
    return (
      <div>
        Authenticated <button onClick={() => signOut()}>Sign Out</button>
      </div>
    );
  }

  return (
    <div>
      Unauthenticated Boi <button onClick={() => signIn()}>Sign in</button>
    </div>
  );
};

export default Home;
``` 

We can then try to sign in by clicking the button **Sign In**

<img width="226" alt="Screenshot 2022-08-09 at 3 43 11 PM" src="https://user-images.githubusercontent.com/45760326/183593217-38b74d87-5233-4614-9131-9c5c6c87f797.png">

This brings us to the sign in page as seen below

<img width="623" alt="Screenshot 2022-08-09 at 3 43 40 PM" src="https://user-images.githubusercontent.com/45760326/183593315-259d0a51-f345-4d98-a6de-2de2c69ccbf1.png">

Which when we fill in our  email redirects us to

<img width="534" alt="Screenshot 2022-08-09 at 3 44 18 PM" src="https://user-images.githubusercontent.com/45760326/183593500-42c1be9c-b751-4933-a8ca-7a4669712ba8.png">

We can verify that our email function works by checking whether we have recieved an email. In this case, I've used a domain `admin@ivanleo.com`.

![Screenshot 2022-08-09 at 3 42 25 PM](https://user-images.githubusercontent.com/45760326/183593096-997e4e45-c76d-4559-a262-9b3761f634e5.png)

Which when we click then allows our user to login and display the **Authenticated** screen as seen below.

<img width="295" alt="Screenshot 2022-08-09 at 3 45 38 PM" src="https://user-images.githubusercontent.com/45760326/183593818-a929a828-0e7b-4867-aeb2-32879107bf06.png">


## Deploying on Vercel

Let's deploy this new project that we've put together. We can do so by using Vercel, which provides a simple One-Click deployment from Github. Simply navigate over and select the project from the New Project tab. You'll then want to just enable the default settings as seen below.

![Screenshot 2022-08-09 at 3 49 20 PM](https://user-images.githubusercontent.com/45760326/183594564-c3b86a4a-26f2-4396-871c-1e59d20957a5.png)


Once everything is built, you'll need to add in every single environment variable which you configured in your .env file to your vercel project. You can do so at **Settings -> Enviroment Variables.**. Now just trigger a redeployment of your project and you should be good to go!