The popular React framework that has dominated developers node_modules
folders over the last few years we all know of is Next.js — and rightly so, with its stellar developer experience, blazing fast compiler, and tons more.
One topic that often comes up in discussions when evaluating if Next.js is right for you is whether or not it can handle authentication. For a long time there wasn't any "documentation" around this, which led to many developers building and pitching their own solutions to handle Next.js Auth.
Let me introduce to you NextAuth.js — Authentication for Next.js. It's completely open source, and you are in control of your data. It boasts itself on being secure, flexible, and easy to use.
Note: You'll need a Hygraph account, and Next.js application if you're following along. You'll also need your Hygraph endpoint, and a Permanent Auth Token with permissions to query and mutate the NextUser
model.
#1. Configure Hygraph
I'll make the assumption you already have a Hygraph account, and project. If not, you know what to do.
We'll opt to store user data from NextAuth.js, including user email and encrypted passwords.
To keep things simple for the purposes of this tutorial, create the model NextUser
with two fields:
Email
/Single line text
/Required
/Unique
Password
/Single line text
/Required
/API Only
Make sure you set the Password field to API only within the advanced visibility settings.
#2. Setup dependencies
We'll be using the following dependencies to configure, and work with NextAuth.js:
next-auth
bcrypt
graphql-request
graphql
npm install next-auth@beta bcrypt graphql-request graphql
Next, create the file .env
and add your Hygraph endpoint and token:
HYGRAPH_ENDPOINT=HYGRAPH_TOKEN=
#3. Configure NextAuth.js
Within your Next.js project, add the file [...nextauth].js
in pages/api/auth
and invoke the CredentialsProvider
.
import NextAuth from "next-auth";import CredentialsProvider from "next-auth/providers/credentials";export default NextAuth({providers: [CredentialsProvider({name: "Email and Password",credentials: {email: {label: "Email",type: "email",placeholder: "jamie@hygraph.com"},password: {label: "Password",type: "password",placeholder: "Password"},},},]})
Now, this code alone won't do anything other than give you an email and password form at the route /api/auth/signin
.
Next you'll want to create (or update if it exists) pages/_app.[js,ts]
to wrap your application with `SessionProvider, passing it the session from the component props:
import { SessionProvider } from 'next-auth/react';export default function MyApp({Component,pageProps: { session, ...pageProps },}) {return (<SessionProvider session={session}><Component {...pageProps} /></SessionProvider>);}
#4. Integrate Hygraph
We'll be working within pages/api/auth/[...nextauth].js
to handle checking Hygraph if a user exists, and if they don't, create one.
Add the following imports:
import { compare, hash } from "bcrypt";import { GraphQLClient } from "graphql-request";
Let's now create a GraphQL client for Hygraph with our endpoint and Permanent Auth Token.
const client = new GraphQLClient(process.env.HYGRAPH_ENDPOINT, {headers: {Authorization: `Bearer ${process.env.HYGRAPH_TOKEN}`,},});
Now, we'll add a query to get a user by email and a mutation to create a user with an email and password:
const GetUserByEmail = gql`query GetUserByEmail($email: String!) {user: nextUser(where: { email: $email }, stage: DRAFT) {idpassword}}`;const CreateNextUserByEmail = gql`mutation CreateNextUserByEmail($email: String!, $password: String!) {newUser: createNextUser(data: { email: $email, password: $password }) {id}}`;
Don't worry, we won't be saving passwords as plain text, but instead using the bcrypt
library we installed to hash, and verify.
Inside of the CredentialsProvider
config below credentials
, add the following authorize
function:
authorize: async ({ email, password }) => {const { user } = await client.request(GetUserByEmail, {email,});if (!user) {const { newUser } = await client.request(CreateNextUserByEmail,{email,password: await hash(password, 12),});return {id: newUser.id,username: email,email,};}const isValid = await compare(password, user.password);if (!isValid) {throw new Error("Wrong credentials. Try again.");}return {id: user.id,username: email,email,};},
#5. Current user UI
Now, let's finish by updating creating an pages/index.js
to show our current user state and links to sign in/out based on our session state.
import { useSession, signIn, signOut } from "next-auth/react";function AuthLinks() {const { data: session, status } = useSession();const loading = status === "loading";if (loading) return null;return (<>{session ? (<p><span>Signed in as {session?.user?.email}</span><button onClick={signOut}>Sign out</button></p>) : (<><button onClick={signIn}>Sign in</button></>)}</>);}export default function IndexPage() {return <AuthLinks />;}
#6. Demo
You should by now have something that looks like this:
There you have it! You've now used NextAuth.js and Hygraph to authenticate your Next.js users.
Blog Author
Jamie Barton
Jamie is a software engineer turned developer advocate. Born and bred in North East England, he loves learning and teaching others through video and written tutorials. Jamie currently publishes Weekly GraphQL Screencasts.