Forem

Cover image for πŸ” Complete Guide to NextAuth in Next.js (Simple Explanation)
Himanay Khajuria
Himanay Khajuria

Posted on

πŸ” Complete Guide to NextAuth in Next.js (Simple Explanation)

When we build a full stack app, we need a way for users to login and stay logged in.

This is called authentication.

In this blog we will understand how to use NextAuth in Next.js in a very simple way 😊


πŸš€ What is NextAuth

NextAuth is a library that helps us:

βœ” Login users

βœ” Manage sessions

βœ” Protect pages

πŸ‘‰ Example

User enters email and password

App checks database

If correct β†’ user is logged in βœ…


πŸ“¦ Step 1 Install NextAuth

Run this command:

npm install next-auth@4
Enter fullscreen mode Exit fullscreen mode

πŸ” Step 2 Add Secret Key

Create or open .env file

NEXTAUTH_SECRET=mySuperSecretKey123
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ This is used to secure login sessions


🧠 Step 3 Add TypeScript Support

Create file:

next-auth.d.ts
Enter fullscreen mode Exit fullscreen mode

Add:

declare module "next-auth" {
  interface Session {
    user: {
      id: string;
    } & DefaultSession["user"];
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Now we can use:

session.user.id
Enter fullscreen mode Exit fullscreen mode

βš™οΈ Step 4 Create Auth Configuration

Create file:

lib/auth.ts
Enter fullscreen mode Exit fullscreen mode
import CredentialsProvider from "next-auth/providers/credentials";
import { connectToDatabase } from "./db";
import User from "@/models/User";
import bcrypt from "bcryptjs";

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "text" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error("Missing email or passsword");
        }

        try {
          await connectToDatabase();
          const user = await User.findOne({ email: credentials.email });

          if (!user) {
            throw new Error("No user found with this");
          }

          const isValid = await bcrypt.compare(
            credentials.password,
            user.password
          );

          if (!isValid) {
            throw new Error("invalid password");
          }

          return {
            id: user._id.toString(),
            email: user.email,
          };
        } catch (error) {
          console.error("Auth error: ", error);
          throw error;
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }: { token: any; user?: any }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }: { session: any; token: any }) {
      if (session.user) {
        session.user.id = token.id as string;
      }
      return session;
    },
  },
  pages: {
    signIn: "/login",
    error: "/login",
  },
  session: {
    strategy: "jwt" as const,
    maxAge: 30 * 24 * 60 * 60,
  },
  secret: process.env.NEXTAUTH_SECRET,
};
Enter fullscreen mode Exit fullscreen mode

This file contains all login logic.


πŸ”‘ Step 5 Credentials Provider

CredentialsProvider({
  name: "Credentials",
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ This tells NextAuth:

πŸ’¬ β€œI will login users using email and password”


πŸ” Step 6 Authorize Function (Main Logic)

async authorize(credentials)
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ This runs when user tries to login


πŸ§ͺ Example Input

{
 "email": "test@gmail.com",
 "password": "123456"
}
Enter fullscreen mode Exit fullscreen mode

🧠 What happens inside authorize

1️⃣ Check input

if (!credentials?.email || !credentials?.password)
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ If missing β†’ error


2️⃣ Connect database

await connectToDatabase();
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Connects to MongoDB


3️⃣ Find user

const user = await User.findOne({ email: credentials.email });
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Check if user exists


4️⃣ If user not found

throw new Error("No user found")
Enter fullscreen mode Exit fullscreen mode

5️⃣ Compare password

bcrypt.compare(credentials.password, user.password)
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Checks password safely πŸ”’


6️⃣ If correct

return {
  id: user._id.toString(),
  email: user.email,
};
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Login success πŸŽ‰


πŸ”„ Step 7 JWT Callback

async jwt({ token, user })
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Runs after login

token.id = user.id;
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Saves user id in token


πŸ‘€ Step 8 Session Callback

async session({ session, token })
Enter fullscreen mode Exit fullscreen mode
session.user.id = token.id;
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Now you can access:

session.user.id
Enter fullscreen mode Exit fullscreen mode

πŸ“„ Step 9 Custom Pages

pages: {
  signIn: "/login",
  error: "/login",
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Redirects user to login page


⏳ Step 10 Session Setup

session: {
  strategy: "jwt",
  maxAge: 30 * 24 * 60 * 60,
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ User stays logged in for 30 days


πŸ”Œ Step 11 Create API Route

Path:

app/api/auth/[...nextauth]/route.ts
Enter fullscreen mode Exit fullscreen mode

Code:

import { authOptions } from "@/lib/auth";
import NextAuth from "next-auth/next";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ This handles all auth requests


πŸ›‘ Step 12 Middleware (Protect Routes)

Create file:

middleware.ts
Enter fullscreen mode Exit fullscreen mode
import withAuth from "next-auth/middleware";
import { NextResponse } from "next/server";

export default withAuth(
    function middleware(){
        return NextResponse.next()
    },
    {
        callbacks: {
            authorized: ({token, req}) => {
                const {pathname} = req.nextUrl;

                // allow auth related routes
                if(
                    pathname.startsWith("/api/auth") ||
                    pathname === "/login" ||
                    pathname === "/register"
                ) {
                    return true
                }

                //public routes
                if(pathname === "/" || pathname.startsWith("/api/videos")){
                    return true
                }

                return !!token
            }
        }
    }
)

export const config = {
    matcher: ["/((?!_next/static|_next/image|favicon.ico|public/).*)"],
};
Enter fullscreen mode Exit fullscreen mode

🧠 What middleware does

πŸ‘‰ Runs on every request

πŸ‘‰ Checks if user is logged in


βœ… Allow public routes

if (
  pathname.startsWith("/api/auth") ||
  pathname === "/login" ||
  pathname === "/register"
)
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ These routes are open


🌍 Public pages

if (pathname === "/" || pathname.startsWith("/api/videos"))
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Anyone can access


πŸ”’ Protect other routes

return !!token
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Only logged in users allowed


πŸ§ͺ Example Flow

πŸ‘‰ User opens:

/dashboard
Enter fullscreen mode Exit fullscreen mode

βœ” If logged in β†’ access

❌ If not β†’ redirect to login


βš™οΈ Matcher Config

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|public/).*)"],
};
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Middleware runs on all important routes


🎯 Final Flow (Easy Understanding)

πŸ‘‰ User enters email and password

➑️ authorize() runs

➑️ database check

➑️ password match

➑️ token created

➑️ session created

➑️ user logged in


πŸ“¦ Example Stored User

{
 "email": "test@gmail.com",
 "password": "$2a$10$hashedpassword"
}
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ Final Summary

In this setup we learned:

βœ” How login works

βœ” How NextAuth handles authentication

βœ” How to connect database

βœ” How to protect routes

βœ” How to manage session


πŸš€ Final Thought

NextAuth makes authentication simple.

Instead of building everything from scratch, you can:

πŸ‘‰ focus on logic

πŸ‘‰ keep your app secure

πŸ‘‰ build faster


Happy coding πŸ’»βœ¨

Top comments (0)