Diagram of Next.js authentication flow using Auth.js and Middleware

Next.js Authentication: 5 Easy Auth.js Steps for Success

Next.js authentication is one of the most important things to get right as a full-stack developer. Whether you’re protecting an admin panel, personalizing a user dashboard, or gating content behind a paywall, you need a system that handles logins, sessions, and route authorization securely.

Building Next.js authentication from scratch means handling password hashing, CSRF tokens, OAuth flows, and cookie encryption yourself. A single mistake in any of these can turn into a serious security vulnerability, and that’s exactly why most developers reach for a battle-tested library instead of rolling their own.

In the Next.js ecosystem, the standard solution for Next.js authentication is Auth.js (formerly NextAuth.js). Built specifically for App Router architecture, Auth.js handles OAuth providers like GitHub and Google, passwordless magic links, and custom credentials right out of the box.

In this guide, we’ll set up Next.js authentication with Auth.js and protect application routes using Next.js Middleware, in five easy steps.

Also Read: If you’re still getting comfortable with the App Router, check out our Next.js Server Components guide before diving into Next.js authentication, since Auth.js leans heavily on server-first patterns.

What You’ll Need Before You Start Next.js Authentication

  • A Next.js 14 or 15 project using the App Router (see our React and Next.js framework introduction if you’re choosing a framework for the first time)
  • Node.js 18.18 or later installed
  • A GitHub OAuth App (or another provider’s credentials) for testing login
  • Basic familiarity with Server Components and Server Actions

How Next.js Authentication Works with Auth.js

Auth.js sits between client interactions, Next.js Middleware, and your backend session store or database, acting as the Next.js authentication layer for your entire app.

Comparison graphic of JWT and database sessions for Next.js authentication
Next.js Authentication: 5 Easy Auth.js Steps for Success 7

Three concepts matter most when you’re learning Next.js authentication with Auth.js:

  • OAuth Providers authenticate users through external services like GitHub or Google, so your app never touches or stores their password.
  • JWT vs. Database Sessions — Auth.js defaults to JSON Web Tokens stored in encrypted, HTTP-only cookies. That keeps session checks fast enough to run on the edge, though you can switch to database sessions if you need instant revocation.
  • Next.js Middleware intercepts incoming requests before they reach a page, letting you check the session token and redirect unauthorized users instantly, before any protected content renders.

Step 1: Install and Configure Auth.js

Start by installing Auth.js for Next.js:

bash

npm install next-auth@beta

Next, create a central auth.js file in your project root:

javascript

// auth.js
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET,
    }),
  ],
  pages: {
    signIn: "/login", // Custom sign-in page path
  },
});

Add your GitHub OAuth credentials and a random AUTH_SECRET value to .env.local. Auth.js needs AUTH_SECRET to encrypt session cookies, so never commit this file to version control.

Step 2: Expose the Auth API Route

To expose the Next.js authentication endpoints (/api/auth/*) that Auth.js relies on, create a catch-all API route handler:

javascript

// app/api/auth/[...nextauth]/route.js
import { handlers } from "@/auth";
export const { GET, POST } = handlers;

This single file handles sign-in, callback, sign-out, and session requests for every configured provider, so you rarely need to touch it again after setup.

Step 3: Protect Routes at the Edge with Middleware

Instead of checking whether a user is logged in inside every single page component, Next.js lets you enforce Next.js authentication across entire route segments with one centralized middleware.js file.

Middleware runs on the edge before a request completes. That makes Next.js authentication checks nearly instant and prevents protected content from ever leaking into the initial HTML.

Next.js Authentication: 5 Easy Auth.js Steps for Success 1
Next.js Authentication: 5 Easy Auth.js Steps for Success 8

javascript

// middleware.js
import { auth } from "@/auth";

export default auth((req) => {
  const isLoggedIn = !!req.auth;
  const isOnDashboard = req.nextUrl.pathname.startsWith("/dashboard");

  if (isOnDashboard && !isLoggedIn) {
    // Redirect unauthenticated users to the login page
    return Response.redirect(new URL("/login", req.nextUrl));
  }
});

// Configure matcher to run middleware on specific paths
export const config = {
  matcher: ["/dashboard/:path*", "/admin/:path*"],
};

Step 4: Access User Sessions in Server and Client Components

Next.js Authentication in Server Components (Zero Client Overhead)

Because Auth.js works seamlessly with React Server Components, reading session data on the server is as simple as calling await auth().

javascript

// app/dashboard/page.js
import { auth } from "@/auth";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const session = await auth();

  // Double-layer protection check directly inside the page
  if (!session) {
    redirect("/login");
  }

  return (
    <main style={{ padding: "20px" }}>
      <h1>Welcome back, {session.user.name}!</h1>
      <p>Logged in as: {session.user.email}</p>
      {session.user.image && (
        <img
          src={session.user.image}
          alt="User avatar for logged-in Next.js authentication session"
          width={50}
          style={{ borderRadius: "50%" }}
        />
      )}
    </main>
  );
}

Checking the session again inside the page, on top of Middleware, is a small bit of redundancy worth keeping. It protects you if the matcher pattern ever falls out of sync with your actual routes.

Server Component showing a logged-in dashboard after Next.js authentication
Next.js Authentication: 5 Easy Auth.js Steps for Success 9

Triggering Login and Logout from Server Actions

For Next.js authentication flows that need explicit buttons, Auth.js exports clean signIn and signOut functions that wire up directly to Server Actions, so you don’t need any client-side JavaScript just to log a user in or out.

javascript

// app/components/AuthButtons.jsx
import { signIn, signOut } from "@/auth";

export function SignInButton() {
  return (
    <form
      action={async () => {
        "use server";
        await signIn("github");
      }}
    >
      <button type="submit">Sign in with GitHub</button>
    </form>
  );
}

export function SignOutButton() {
  return (
    <form
      action={async () => {
        "use server";
        await signOut();
      }}
    >
      <button type="submit">Log Out</button>
    </form>
  );
}

Step 5: Handle Common Setup Errors

A few errors show up often enough during Next.js authentication setup that they’re worth calling out directly:

  • “MissingSecret” error – Usually means AUTH_SECRET isn’t set in .env.local, or your dev server started before the file was saved. Restart the server after adding it.
  • OAuth callback URL mismatch – Your GitHub OAuth App’s callback URL must exactly match http://localhost:3000/api/auth/callback/github in development, protocol and port included.
  • Session is null on the client – Client Components need a SessionProvider wrapper if you’re reading session state with a hook instead of await auth() on the server. Prefer the server-side approach shown above where you can.

Also Read: New to the App Router itself? Our ReactJS and Next.js project setup guide walks through the foundation this Next.js authentication setup builds on.

Summary Checklist for Auth.js

Next.js Auth Setup Files

Next.js Auth Setup Files

The four files that power a complete authentication system

01
Auth configuration
File Path
auth.js
Purpose
Configures OAuth providers, JWT options, and custom pages
02
API route handler
File Path
app/api/auth/[…nextauth]/route.js
Purpose
Exposes standard HTTP auth endpoints
03
Edge protection
File Path
middleware.js
Purpose
Protects route patterns globally before the page renders
04
Session retrieval
File Path
await auth() inside page.js
Purpose
Fetches active user data inside Server Components
Task File Path Purpose
01
Auth configuration
auth.js Configures OAuth providers, JWT options, and custom pages
02
API route handler
app/api/auth/[…nextauth]/route.js Exposes standard HTTP auth endpoints
03
Edge protection
middleware.js Protects route patterns globally before the page renders
04
Session retrieval
await auth() inside page.js Fetches active user data inside Server Components

Frequently Asked Questions

Is Auth.js free to use in production?
Yes. Auth.js is open source and free under the ISC license. You pay nothing to Auth.js itself; any cost comes from your own database or hosting if you choose database sessions over JWT.

Does Next.js authentication with Auth.js work with the Pages Router?
Yes, though this guide focuses on the App Router. The core configuration in auth.js stays the same; only the API route and middleware syntax differ slightly for the Pages Router.

Can I use email and password login instead of OAuth?
Yes. For Next.js authentication without a third-party provider, Auth.js supports a Credentials provider for username-and-password login. You’re responsible for hashing passwords and validating credentials yourself, since Auth.js doesn’t manage a user database for you.

Why use Middleware instead of checking auth on every page?
Middleware centralizes Next.js authentication logic in one file instead of repeating the same check across dozens of pages, and it runs at the edge, so unauthorized users get redirected before any protected markup is ever generated.

Do I need a database for Next.js authentication?
Not necessarily. JWT sessions store everything in an encrypted cookie, so you can skip a database entirely for small projects. You’ll need one only if you want database sessions, instant token revocation, or to persist user data beyond the session.

Conclusion

Auth.js makes Next.js authentication fast, flexible, and genuinely secure without forcing you to build password hashing or CSRF handling by hand. By combining OAuth providers, edge Middleware for route protection, and Server Components for session checks, you get a setup that’s both simpler to maintain and safer by default.

Start with the GitHub provider above, get one protected route working end to end, then add more providers or a Credentials flow once the core pattern clicks.

Content Protection by DMCA.com
Spread the love
Scroll to Top
×