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 and create a free account. Once you've done so, simply go over to the dashboard and create a new project.
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.
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.
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. Once you've created a new account, navigate to the SES dashboard and click on Verified Identities.
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
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
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.
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 - 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
This brings us to the sign in page as seen below
Which when we fill in our email redirects us to
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
.
Which when we click then allows our user to login and display the Authenticated screen as seen below.
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.
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!
Top comments (0)