Setting up NextAuth.js 5.0 with Next.js 14

Next.Js 14 was recently released on October 26th and if you were thinking about making an upgrade, there is still a lack of good information and documentation on some key functionalities you will likely need. In this blog, I will outline how to implement version 5.0 of NextAuth.js in the latest version of Next.js

Step #1 – Create Auth Configuration

Create a file in the root call auth.config.ts

import type { NextAuthConfig } from 'next-auth';

export const authConfig = {
   providers: [],
   pages: {
       signIn: '/login',
   },
   callbacks: {
       authorized({ auth, request: { nextUrl } }) {
           const isLoggedIn = !!auth?.user;
           if (!isLoggedIn) {
               Response.redirect(new URL('/login', nextUrl));
               return false; //
           }
           return true;
       },
   },
} satisfies NextAuthConfig;

In this file, we are creating an authconfig object and defining a callback that will protect any routes from unauthorized use. Then we will use this to initialize our auth object.

Step #2 - Create auth object

Create a file on the root called auth.ts that implements your auth config.

import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { authConfig } from './auth.config';
import { z } from 'zod';

export const { auth, signIn, signOut } = NextAuth({
   ...authConfig,
   providers: [
       Credentials({
           async authorize(credentials) {
               const parsedCredentials = z
                   .object({ email: z.string().email(), password: z.string().min(6) })
                   .safeParse(credentials);

               if (parsedCredentials.success) {
                   const { email, password } = parsedCredentials.data;
                   const user = await getUser(email);
                   if (passwordsMatch) return user;
               }
               return null;
           },
       }),
   ],
});

Here we are consuming our authconfig as well as defining a simple Credentials provider that will handle logging in. The authorized method here is the code that will run when we call sign-in. If this succeeds the session will be authorized and the user information will be stored in the session.

Step #3 - Create Middleware

Create a file in the root called middlware.ts

import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
   // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
   matcher: ['/((?!api|_next/static|_next/image|.png).*)'],
};

Here we are adding our auth class and matching on all routes that aren’t api.next/static/image etc.

This allows the callback we defined in our auth.config.ts to run before every request. If we want to exclude a route from the authorization check, we can simply update the matcher.

Step #4 - Call Auth to get auth status

Now we can use NextAuth.js in our components. To sign in we can call signIn from our auth object. This is available everywhere, just need to import it.

import { signIn } from '@/auth';

signOut would function the same. To access our authentication status, we can simply import auth from our auth object.

import { auth } from '@/auth';
export default async function Page() {
   console.log('USER', await auth())
   return (
       <main>
       </main>
   );
}
USER {
 user: { name: 'USERNAME', email: 'EMAIL' },
 expires: '2023-12-17T17:01:13.803Z'
}

We can store whatever we want in this object and access it from anywhere to check if the user is logged in or not. We no longer must configure a session provider or deal with server/client requests. With this simple set up we have exposed an authentication manager to the entire application that can protect routes and allow for conditional rendering.

Additional resources

  • https://nextjs.org/blog/next-14

  • https://github.com/nextauthjs/next-auth/discussions/8487