DEV Community

Cover image for Adding Authentication to an existing serverless Next.js App in no time with NextAuth.js!
Nico Domino
Nico Domino

Posted on • Edited on

Adding Authentication to an existing serverless Next.js App in no time with NextAuth.js!

Updated 25.05.21 with latest NextAuth.js examples / best practices / links

Do you have a Next.js application and you'd like to add user / authentication support? Well look no further! All you need is NextAuth.js, some OAuth keys from your favorite provider (Google, Github, etc.) and about 5 minutes of your time!

Let's summarize what we're going to do here before we get started.

We're going to do the following things in this tutorial:

  1. Create OAuth keys in the Google Developer Portal
  2. Install and configure NextAuth.js into our Next.js application
  3. Protect some page routes and api routes

Let's get started!

Prerequisites

I'm going to assume you have the following already:

  1. A Google Account
  2. An existing Next.js app

Google OAuth Setup

So first we have to obtain our OAuth clientID and secret from Google. NextAuth.js has built-in support for a ton of OAuth providers like Apple, Discord, Facebook, Github, Google, LinkedIn, Okta, Slack, Twitch, Twitter, and a ton more which you can find in their docs - providers. There are instructions on how to retrieve the required OAuth keys for all providers there, but I will only go into detail for how to setup Google here.

First, visit https://console.developers.google.com/apis/credentials.

If you don't have a project setup in Google's developer console yet or you want to create a new one for this application, go ahead and select the project overview in the top left, and click "Create Project"

Create Project

Once you have the project created, make sure you're in the "API's & Services" -> "Credentials" section of the left-hand main menu. There, select "Create Credentials" and select "OAuth client ID".

Create Credentials

If you've not yet gotten the OAuth consent screen setup for your domain, you'll have to do that first - just enter a name for the app and the domain you'll be using for it. For Internal applications there is no Google verification process necessary, but it will also only allow members of your G Suite domain to use the application. Public applications must have the domain verified by Google before large scale sign-in is allowed - OAuth is limited to 100 "sensitive scope logins" i.e. test logins until the consent screen is verified by Google.

Next, select "Web Application" for the type of OAuth Client ID we'd like to create, and give it a name. You'll also have to enter a URI for Authorized Javascript Origins and Authorized redirect URIs here.

The Authorized Javascript Origin is simply the domain of your application, for example myapp.domain.com.

The Authorized redirect URI will vary depending on which provider you're setting up, but for NextAuth.js's default setup with Google as your OAuth provider, the redirect URI will be something like: https://myapp.domain.com/api/auth/callback/google. Obviously make sure to adjust the domain to whatever you're running your application under, the important part is the /api/auth/callback/google path.

Create OAuth Client

After you press "Create", you'll get your OAuth Client ID and OAuth Secret! Make sure to save these somewhere because we'll need them later!

NextAuth.js Install

Next, we're going to need to install NextAuth.js into our Next.js application. So for that, navigate to your project directory and install next-auth.

cd /opt/dev/my-nextjs-app
npm i -s next-auth
Enter fullscreen mode Exit fullscreen mode

Now we're ready for the NextAuth.js setup!

Initial Setup

Now that we've got all the prerequisites and dependencies out of the way, lets get the config file for NextAuth.js setup. First, we'll need a .env file in the root of your project directory if you don't have one yet.

Your .env file should contain the following:

NEXTAUTH_URL=https://myapp.domain.com
GOOGLE_ID=abc123
GOOGLE_SECRET=abc123
Enter fullscreen mode Exit fullscreen mode

The NEXTAUTH_URL is the URL at which your application will be running. You can set multiple URLs for different environments by setting up multiple .env files, like .env.production, .env.development, etc. The same is possible in almost any hosting provider's environment variables setup section (i.e. with Netlify or Vercel, etc.). The GOOGLE_ID and GOOGLE_SECRET are our OAuth keys we received from the Google developer console earlier!

Next, create a Next.js API route for authentication at pages/api/auth/[...nextauth].js. This file should contain at least the following:

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'

const options = {
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET
    }),
  ],
  debug: false
}

export default (req, res) => NextAuth(req, res, options)
Enter fullscreen mode Exit fullscreen mode

There's a lot more you can configure here, but this is the minimum requirements for JWT based OAuth authentication. More config options can be found in the docs - configuration, as well as a fully annotated version of this file here.

The keen eyed among you will notice we did not specify any sort of database here. This means a few things:

  1. JWT Support is enabled by default for session handling
  2. We do not have a permanent database of users

If you'd like to permanently store the users/accounts in a database, NextAuth.js makes it really easy to add sqlite for testing or various other adapters such as MySQL, Postgres, MongoDB (via TypeORM), FaunaDB, Firebase, DynamoDB, and Prisma. More information on database details can be found in their docs - adapters.

Next, you'll also want to wrap your app in a NextAuth provider, so open up your pages/_app.js or create one if you don't have one yet.

It should end up looking something like this:

import { Provider } from 'next-auth/client'

export default function App ({ Component, pageProps }) {
  return (
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

By wrapping your whole application in this provider, we can use React Context to make a useSession hook available in any page / component in your app and share session state.

Believe it or not, this is all that's needed for a basic Google OAuth setup with NextAuth.js!

The library includes some basic Login / Logout pages by default, so there's no need to write them. If you'd like to customize those, that is of course supported. Please take a look here for more details.

The last thing we're going to do in this tutorial is to show how you can actually restrict access to pages to authenticated users only.

Currently users can still navigate to any route in our app and will get everything rendered no matter their logged in status because we're not conditionally rendering anything yet.

Restricting Page Access

First, let's build a little AccessDenied component which we'll show the user if they land on a protected route and have not logged in yet. This will tell them they do not have access, and give them an opportunity to sign-in.

Lets name this, components/accessDenied.js

import { signIn } from 'next-auth/client'

export default function AccessDenied () {
  return (
    <>
      <h1>Access Denied</h1>
      <p>
        <a href="/api/auth/signin"
           onClick={(e) => {
           e.preventDefault()
           signIn()
        }}>You must be signed in to view this page</a>
      </p>
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Finally, on our pages that we want to protect, we just need to import the useSession hook from next-auth and check for the session before returning the page contents, showing the accessDenied.js component if there is no session available.

Client-side Page

An example protected client-side rendered page (i.e. pages/index.js) may look like this:

import React from 'react'
import { useSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'

export default function Page () {
  const [ session, loading ] = useSession()

  // When rendering client side don't display anything until loading is complete
  if (typeof window !== 'undefined' && loading) return null

  // If no session exists, display access denied message
  if (!session) { return  <Layout><AccessDenied/></Layout> }

  // If session exists, display content
  return (
    <Layout>
      <h1>Protected Page</h1>
      <p><strong>Welcome {session.user.name}</strong></p>
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode

SSR Page

And an example SSR page may look like this:

import { getSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'

export default function Page ({ session }) {
  // If no session exists, display access denied message
  if (!session) { return  <Layout><AccessDenied/></Layout> }

  // If session exists, display content
  return (
    <Layout>
      <h1>Protected Page</h1>
      <p><strong>Welcome {session.user.name}</strong></p>
    </Layout>
  )
}

export async function getServerSideProps(context) {
  const session = await getSession(context)

  return {
    props: {
      session
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

API Route

And finally, an example protected API Route may look like this:

// pages/api/protected.js
import { getSession } from 'next-auth/client'

export default async (req, res) => {
  const session = await getSession({ req })

  if (session) {
    res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
  } else {
    res.send({ error: 'You must be signed in to access this api route' })
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Alright everyone, I hope that didn't take much longer than the promised 5 minutes!

This example can be deployed to Vercel for example, as NextAuth.js is compatible with their serverless api functions.

I'd also be remiss if I didn't mention that alongside built-in support for the many OAuth providers I mentioned in the introduction, there is also built-in support for Email based "magic link" sign-in as well as options for adding custom OAuth providers and other custom authentication providers, including a great tutorial detailing how to use LDAP.

I want to thank @iaincollins and @balazsorban and all the wonderful contributors to NextAuth.js for their great work! This post was based mostly off the example app and my experience using NextAuth.js in 2-3 Next.js applications as well as contributing to the project over the last few months.

You can find more information in the official NextAuth.js example application here.

Further Reading

Top comments (9)

Collapse
 
btakashi profile image
Brian Takashi Hooper

Hey Nico, great article, very useful! I was trying this out and noticed that the _app.js snippet should now be:

? (not sure if this is due to recent changes, apologies if it is an inconvenience to maintain old posts...)

Collapse
 
vncafecode profile image
vncafecode

I think it's versioning. The newest is v4 and the example looks like v3.
There's something you will run into:
next-auth/clientnext-auth/react
ProviderSessionProvider

Collapse
 
roshanyadav1 profile image
Roshan Yadav

hy , i have an issue that how can i provide using app router , i am still in a confusion anyone try to resolve this question how its done .

Collapse
 
g4ndy profile image
g4ndy

Thank you, nice and short article.

Collapse
 
rashil2000 profile image
Rashil Gandhi • Edited

The NextAuth.js link is broken.
Correct one is next-auth.js.org

Collapse
 
ndom91 profile image
Nico Domino

Thanks for the heads up! Fixed.

Collapse
 
dlcmh profile image
David Chin

Bookmarked!

Definitely the best one out there, especially the last sections on securing client-side & SSR pages, and API routes.

Thank you.

Collapse
 
mahaack79 profile image
Brian Haack

is there an update to this for nextjs 13 with app folder structure using route grouping ?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.