Editor’s note: This guide was last updated on 13 June 2023 to include information about creating a custom login page with Next.js, and information about Next.js Middleware, which was released in v12.
Authentication is an important and sensitive feature in applications where a user’s credentials, such as username, email, and password are used to verify their identity.
In this article, we’ll set up client-side authentication that doesn’t require a password in Next.js using a powerful and secure library called NextAuth.js. Our app will allow users to log in using their GitHub, Google, and Facebook accounts. Upon successful signup, we will display the user’s profile picture and email, which we’ll retrieve from their social media accounts.
To build our app, we will use React Hooks and functional components.
Jump ahead:
- What is Next.js?
- What is NextAuth.js?
- Creating the Next.js starter application
- Setting up authentication with NextAuth.js
- Understanding the Nextauth_secret environment
- Creating a custom login page with NextAuth
- Understanding NextAuth Middleware
What is Next.js?
Next.js is a framework built on top of React that makes developing production-ready, fully-optimized React apps fast and easy. It is one of the best things to have come out of the React ecosystem, as it’s very powerful with zero config.
Next.js is used in production by top companies like Netflix, Tiktok, and Nike.
What is NextAuth.js?
NextAuth.js is a completely secured and flexible authentication library designed to sync with any OAuth service, with full support for passwordless signin.
NextAuth.js can be used with or without a database, and it has default support for popular databases such as MySQL, MongoDB, PostgreSQL, and MariaDB. It can also be used without a database by syncing with services like OAuth and JSON Web Token.
How does NextAuth.js work?
With a library like NextAuth.js, you don’t need to be an expert in identity protocol like you would if you were to use OAuth to build secured Next.js applications. NextAuth.js is built to avoid the need to store sensitive data, such as a user’s password. It works perfectly with Next.js. With just 20 lines of code, you can implement an authentication feature in your app.
NextAuth.js has a client-side API you can use to interact with sessions in your app. The session data returned from the Providers contains user payload, and this can be displayed to the user upon successful login.
The session data returned to the client looks like this:
{ expires: '2055-12-07T09:56:01.450Z'; user: { email: '[email protected]'; image: 'https://avatars2.githubusercontent.com/u/45228014?v=4'; name: 'Ejiro Asiuwhu'; } }
The payload doesn’t contain any sensitive data. The session payload or data is meant for presentation purposes — that is, it’s meant to be displayed to the user.
NextAuth.js also provides the useSession()
React Hook, which can be used to check user login status. Meanwhile, NextAuth.js provides a REST API that is used by the React app. To learn more about what the REST API NextAuth exposes, check out the official docs.
Requirements
- Node.js installed on your local machine
- A basic understanding of React.js
Creating the Next.js starter application
To get started, create a new Next.js application by running the command below:
npx create-next-app@latest
You will be prompted to go through a series of questions; your selections should the same as the ones from the screenshot below:
Now, change the directory into the project folder and launch the development server:
npm run dev # or yarn run dev
By default, the project will run on port 3000
. Launch your browser and navigate to http://localhost:3000
. You should end up with this:
Setting up authentication with NextAuth.js
Now that we have the Next.js starter application set up, we’re ready to learn how to authenticate a Next.js app with NextAuth.js.
This NextAuth.js client-side authentication tutorial will cover the following:
- Installing NextAuth.js
- Creating a GitHub OAuth app
- Creating a Google OAuth app
- Creating a Facebook OAuth app
Installing NextAuth.js
next-auth
is an npm package, so installing it is easy:
npm install next-auth # or yarn add next-auth
In our demo, we’ll give users the choice to log in to our app using their GitHub, Google, or Facebook account.
Creating a GitHub OAuth app
Next, we’re going to add a GitHub Authentication Provider, which essentially allows users to log in to our app using their GitHub account. But first, we need to create a GitHub OAuth app. Click on New OAuth app and fill out the form accordingly. Check out the official docs to learn more:
- Application name: This is the name of your application. It can be called anything
- Homepage URL: This is the full URL to the homepage of our app. Because we are still in development mode, we are going to fill in the full URL that our development server is running on. In this case, it is
http://localhost:3000
- Authorization callback URL: This is the URL that GitHub will redirect our users to after they have been successfully logged in to our app. It should be your homepage URL plus
/api/auth/callback
, resulting inhttp://localhost:3000/api/auth/callback
After registering our OAuth app, GitHub creates a Client ID and Client Secret specifically for our newly created app. Copy the Client ID and secret key to your clipboard. Click on Generate new client secret to get a Client Secret.
Understanding the Nextauth_secret environment
NEXTAUTH_SECRET
is a critical environment variable used by NextAuth.js to sign and verify JWTs (JSON Web Tokens) and encrypt session data. This value should ideally be a long, random string that is kept secure and never exposed publicly. To generate a secret, you can use the Node.js built-in crypto module on your terminal as follows:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
This generates a secure, random, 32-byte value, encoded as a hexadecimal string. Set this string as the value of the NEXTAUTH_SECRET
environment variable in your .env.local
file with your GitHub client and secret keys:
GITHUB_ID=<GITHUB_CLIENT_ID> GITHUB_SECRET=<GITHUB_CLIENT_SECRET> NEXTAUTH_URL=http://localhost:3000 NEXTAUTH_SECRET=<NEXTAUTH_SECRET_KEY>
Replace <GITHUB_CLIENT_ID>
, <GITHUB_CLIENT_SECRET>
, and <NEXTAUTH_SECRET_KEY>
with your GitHub client, secret key, and your NextAuth secret.
Note: If you don’t provide a NEXTAUTH_SECRET
variable, NextAuth.js will automatically generate one for you when the server starts. However, this dynamically generated secret won’t persist across server restarts. This means that all your signed tokens and sessions will become invalid every time your server restarts, as they will fail the verification against the new secret. Therefore, it is strongly recommended to provide your own NEXTAUTH_SECRET
variable for consistent session and token management in production environments.
Now, back to our app. Create an api/auth/[…nextauth].tsx
file in pages
and add the following code:
import NextAuth from "next-auth"; import type { NextAuthOptions } from "next-auth"; import GitHubProvider from "next-auth/providers/github"; export const authOptions: NextAuthOptions = { providers: [ GitHubProvider({ clientId: process.env.GITHUB_ID as string, clientSecret: process.env.GITHUB_SECRET as string, }), ], secret: process.env.NEXTAUTH_SECRET, } const handler = NextAuth(authOptions); export { handler as GET, handler as POST };
In the above code snippet, GitHubProvider
is imported from "next-auth/providers/github"
and is included in the providers
array of the authOptions
configuration object, which is of type NextAuthOptions
. This object is passed to the NextAuth
function, creating a handler for processing authentication requests. The client ID and secret for GitHub are sourced from the environment variables GITHUB_ID
and GITHUB_SECRET
. Lastly, the handler is exported under the aliases GET
and POST
, indicating it will manage both types of HTTP requests for authentication routes.
Then, navigate to http://localhost:3000/api/auth/signin
and you should see this:
Checking user login state with the useSession()
Hook
We need to get the login state of our users and render user details on the frontend of our app. To achieve this, NextAuth needs a session provider to be configured on the client-side and wrapped around our component.
Once this is done, we can check user login state and obtain session information using the useSession()
Hook. Let’s start by updating the _app.tsx
file to a provider.ts
file in the app directory and adding the code snippet below:
import { SessionProvider } from 'next-auth/react'; import '../styles/globals.css'; import type { AppProps } from 'next/app'; import React from 'react'; export default function App({ Component, pageProps: { session, ...pageProps }, }: AppProps) { return ( <SessionProvider session={pageProps.session}> <Component {...pageProps} /> </SessionProvider> ); }
In the above code snippet, we created a wrapper component (NextAuthProvider
) around the SessionProvider
component from the next-auth/react
package. This will provide session state to all components in your application, allowing you to access session data using the useSession()
Hook.
Let’s make use of the useSession()
Hook. Replace the code in the index.tsx
file with the following code:
import Link from "next/link"; import { useSession, signIn, signOut } from "next-auth/react"; export default function Home() { const { data: session } = useSession(); return ( <main style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "70vh", }} > <div className="header"> <Link href="/"> <p className="logo">NextAuth.js</p> </Link> {session && ( <Link href="#" onClick={() => signOut()} className="btn-signin"> Sign out </Link> )} {!session && ( <Link href="#" onClick={() => signIn()} className="btn-signin"> Sign in </Link> )} </div> </main> ); }
In the above code snippet, we imported the next-auth hooks signIn
, signOut
, and useSession
. The signIn
Hook redirects you to the GitHub login page, signOut
destroys the activate session and logs the user out, while useSession
provides us with the details of the logged in user. So, we used useSession
to check if a session exists and conditionally render the signin and and signout links.
More great articles from LogRocket:
- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Discover how to use the React children prop with TypeScript
- Explore creating a custom mouse cursor with CSS
- Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Creating a custom login page with NextAuth
So far in your code, you’ve been using the custom login page provided by NextAuth. Now, let’s create a custom login page for your application. To do that, modify the pages/api/auth/[…nextauth].ts
file to add a new option to the NextAuth configuration:
... export const authOptions: NextAuthOptions = { providers: [ ... ], secret: process.env.NEXTAUTH_SECRET, pages: { signIn: "/auth/signin", }, } ...
Here, you overrode the default pages that NextAuth.js uses for various authentication-related screens. In this case, the signIn
property is being set to "/auth/signin"
, which means that the sign-in page is customized, and NextAuth.js will redirect users to this page when authentication is required.
Now, create a auth/signin.tsx
file in the pages
directory and add the code snippet below:
import type { GetServerSidePropsContext, InferGetServerSidePropsType } from "next"; import { getProviders, signIn } from "next-auth/react" import { getServerSession } from "next-auth/next" import { authOptions } from "../api/auth/[...nextauth]"; export default function SignIn({ providers }: InferGetServerSidePropsType<typeof getServerSideProps>) { return ( <> {Object.values(providers).map((provider) => ( <div key={provider.name}> <button onClick={() => signIn(provider.id)}> Sign in with {provider.name} </button> </div> ))} </> ) } export async function getServerSideProps(context: GetServerSidePropsContext) { const session = await getServerSession(context.req, context.res, authOptions); // If the user is already logged in, redirect. // Note: Make sure not to redirect to the same page // To avoid an infinite loop! if (session) { return { redirect: { destination: "/" } }; } const providers = await getProviders(); return { props: { providers: providers ?? [] }, } }
The above code uses server-side rendering to fetch a list of available providers (such as Google, Facebook, etc.) and display them as sign-in buttons. If a user is already logged in, they are redirected to the homepage. The getServerSideProps
function fetches the providers and handles session management before the page is rendered. Now if you click on the signin link, you’ll be redirected to the custom signin page.
Understanding NextAuth Middleware
NextAuth Middleware is a key feature of the NextAuth.js library, designed to boost the security of your Next.js apps. Introduced in Next.js 12, it allows you to run certain code before any page (even static ones) is loaded, thus improving both the speed and safety of your website.
The power of Middleware is magnified when used with platforms like Vercel, where it operates at the edge network level, meaning it’s closer to your users, which further enhances performance. One of the main roles of NextAuth Middleware is managing authentication; it verifies users and controls their access to specific parts of your site.
To add Middleware to your Next.js application using NextAuth, create a middleware.ts
file in the src
folder and define your middleware as follows:
import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith('/profile')) { // Add /profile specific logics } if (request.nextUrl.pathname.startsWith('/dashboard')) { // Add /dashboard specific logics } } export const config = { matcher: ['/profile/:path*', '/dashboard/:path*'], }
The above code defines a middleware function that interacts with incoming HTTP requests for paths beginning with /profile
and /dashboard
. Using the NextRequest
object from next/server
, it accesses the path of the requested URL. For any path that starts with /profile
or /dashboard
, the corresponding code inside the conditionals executes for these specific paths.
Lastly, the matcher
property of the config
object dictates which paths, including any subpaths under /profile
and /dashboard
, the middleware function should be applied to.
Conclusion
In this post, we implemented user authentication in Next.js using NextAuth.js, which is a secured library to identify our users, get user profile information, and render them in our frontend. We covered most of the use cases, but there is a lot more you can do with NextAuth.js, such as adding a database using JSON Web Token, securing pages, and more.
Let me know in the comment section below what you thought of this tutorial. You can also reach me on Twitter and GitHub. Thank you for reading!
Thanks for the write up
Great article! However, you called it ‘Nuxt-Auth.js’ and ‘Next-Auth.js’ a few times, it is ‘NextAuth.js’
– core team member
Thanks for pointing that out, I’ve fixed the instance of `Nuxt-Auth.js`
Does next-auth work with only SSG?
It has full support for SSR and SSG
Really great article 😊 I really enjoyed reading it and it makes me want to switch to Next.js. I was wondering why you called this client-side rather than severless? Is the authentication not done server side by SSR? I really apologize if I’m wrong, I’m just trying to understand the authentication flow.
Just a heads up that _ap.js should actually be _app.js. Thanks for the post!
👍
website: next-auth.js
Example code : next-auth-example
npm package : next-auth
But it is called NextAuth.js and not next-auth.js as one could guess from all your urls.
Am I missing something here ?
I’m wondering the same. Needing a secret is a red flag that this is running in the backend.
How is it supposed to run in frontend?
Unhandled Runtime Error
Error: React Context is unavailable in Server Components
Call Stack
SessionProvider
can you please resolve this error
Use the “use client” flag at the top of the file.
That error is showing up because “SessionProvider” is using react context which is a client component and for it to work, you must mark the file as a client component with the flag above.
You are using react 13.+.+ I believe