Choorai
Lv.3 Next.js

NextAuth.js Auth Guide

NextAuth.js (Auth.js) is a full-stack authentication solution for Next.js. It supports OAuth, email login, and DB sessions.

Next.js Only

NextAuth.js can only be used with Next.js. For plain React apps, use Clerk.

Why NextAuth?

  • Full support for Next.js App Router
  • 50+ OAuth provider support
  • Session access in Server Components
  • DB adapters for Prisma, Drizzle, etc.
  • Free & open-source

1Installation & Environment Variables

Terminal
npm install next-auth

Environment Variables

.env.local
# .env.local
NEXTAUTH_SECRET=your-secret-key-here
NEXTAUTH_URL=http://localhost:3000

# Google OAuth (optional)
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx

# GitHub OAuth (optional)
GITHUB_ID=xxx
GITHUB_SECRET=xxx

Info

NEXTAUTH_SECRET can be generated with openssl rand -base64 32.

2API Route Setup

NextAuth uses the /api/auth/* route.

app/api/auth/[...nextauth]/route.ts
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import GitHubProvider from 'next-auth/providers/github';
import CredentialsProvider from 'next-auth/providers/credentials';

const handler = NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
    // Email/password login (custom implementation)
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        // Query DB and verify password here
        // In practice, use bcrypt for hash comparison
        const user = await findUserByEmail(credentials?.email);
        if (user && verifyPassword(credentials?.password, user.password)) {
          return { id: user.id, email: user.email, name: user.name };
        }
        return null;
      },
    }),
  ],
  pages: {
    signIn: '/login',  // Custom login page
    error: '/auth/error',
  },
  callbacks: {
    async session({ session, token }) {
      // Add user ID to session
      if (token.sub) {
        session.user.id = token.sub;
      }
      return session;
    },
  },
});

export { handler as GET, handler as POST };

3SessionProvider Setup

To use sessions in client components, SessionProvider is required.

app/providers.tsx & app/layout.tsx
// app/providers.tsx
'use client';

import { SessionProvider } from 'next-auth/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}

// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

4Using Sessions on the Client

components/AuthStatus.tsx
// components/AuthStatus.tsx
'use client';

import { useSession, signIn, signOut } from 'next-auth/react';

export function AuthStatus() {
  const { data: session, status } = useSession();

  if (status === 'loading') {
    return <div>Loading...</div>;
  }

  if (status === 'unauthenticated') {
    return (
      <div className="flex gap-2">
        <button
          onClick={() => signIn('google')}
          className="px-4 py-2 bg-white text-gray-800 rounded-lg border"
        >
          Sign in with Google
        </button>
        <button
          onClick={() => signIn('github')}
          className="px-4 py-2 bg-gray-800 text-white rounded-lg"
        >
          Sign in with GitHub
        </button>
      </div>
    );
  }

  return (
    <div className="flex items-center gap-4">
      <span>Hello, {session?.user?.name}!</span>
      <button
        onClick={() => signOut()}
        className="px-4 py-2 bg-red-600 text-white rounded-lg"
      >
        Sign Out
      </button>
    </div>
  );
}

5Using Sessions in Server Components

In Server Components, use getServerSession.

app/dashboard/page.tsx
// app/dashboard/page.tsx
import { getServerSession } from 'next-auth';
import { redirect } from 'next/navigation';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';

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

  if (!session) {
    redirect('/login');
  }

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Welcome, {session.user?.name}!</p>
    </div>
  );
}

6Route Protection with Middleware

Use middleware to protect specific routes in bulk.

middleware.ts
// middleware.ts
import { withAuth } from 'next-auth/middleware';

export default withAuth({
  callbacks: {
    authorized: ({ token }) => !!token,
  },
});

// Specify routes to protect
export const config = {
  matcher: ['/dashboard/:path*', '/settings/:path*', '/api/protected/:path*'],
};

7Database Integration (Optional)

To store user information in a database, use an adapter.

Prisma Adapter Setup
// 1. Install packages
// npm install @prisma/client @next-auth/prisma-adapter
// npm install -D prisma

// 2. Prisma schema
// prisma/schema.prisma
model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?
  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

// 3. Add adapter to NextAuth config
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import { prisma } from '@/lib/prisma';

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [...],
});

When Should You Choose NextAuth?

NextAuth Recommended

  • Next.js full-stack projects
  • Need sessions in Server Components
  • Need to store user info in DB
  • Prefer a free solution

Consider Alternatives

Next Steps

Last updated: February 22, 2026 · Version: v0.0.1

Send Feedback

Opens a new issue page with your message.

Open GitHub Issue