How to Add Secure Authentication to Next.js

Share
How-To Guide

How to Add Secure Authentication to Next.js

Using NextAuth.js (Auth.js) for App Router

TL;DR

TL;DR

Use NextAuth.js for authentication. Set NEXTAUTH_SECRET in production. Use middleware to protect routes. Always verify sessions on API routes too. Check authorization (not just authentication) before accessing resources.

Prerequisites

You'll need a Next.js 13+ app with App Router. This guide uses NextAuth.js v4 (also known as Auth.js).

Step-by-Step Guide

1

Install NextAuth.js

npm install next-auth
2

Create the auth configuration

Create app/api/auth/[...nextauth]/route.ts:

import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { NextAuthOptions } from 'next-auth';

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      // Add user ID to session
      if (session.user) {
        session.user.id = token.sub!;
      }
      return session;
    },
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
3

Set up environment variables

Add to .env.local:

# Generate with: openssl rand -base64 32
NEXTAUTH_SECRET=your-super-secret-key-here
NEXTAUTH_URL=http://localhost:3000

# OAuth provider credentials
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret

Critical: NEXTAUTH_SECRET must be set in production. Without it, your sessions are not secure.

4

Protect routes with middleware

Create middleware.ts in your project root:

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

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

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

Get session in Server Components

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

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

  if (!session) {
    redirect('/api/auth/signin');
  }

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

Protect API routes

import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const session = await getServerSession(authOptions);

  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Now check authorization (does this user own this resource?)
  const userId = session.user.id;

  // Fetch data scoped to this user
  const data = await db.posts.findMany({ where: { authorId: userId } });

  return NextResponse.json(data);
}
7

Add session provider for client components

Create app/providers.tsx:

'use client';

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

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

Wrap your app in app/layout.tsx:

import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Common Security Mistakes

  • Missing NEXTAUTH_SECRET in production - Sessions won't be secure
  • Only checking auth on frontend - API routes need auth checks too
  • Forgetting authorization - Auth proves who you are, but you still need to check if they can access the resource
  • Exposing session data - Don't put sensitive info in the JWT/session that goes to the client

Testing Your Auth

  1. Try accessing /dashboard without logging in - should redirect
  2. Try calling API routes without a session - should return 401
  3. Try accessing another user's data - should be denied
  4. Check browser DevTools for exposed tokens or sensitive data
How-To Guides

How to Add Secure Authentication to Next.js