Skip to main content

Endpoint

GET/POST /api/auth/*
NextAuth.js provides a catch-all route handler at /api/auth/[...nextauth] that manages all authentication-related requests.

Authentication Providers

Google OAuth Provider

Enables Google account authentication using OAuth 2.0.

Configuration

GOOGLE_CLIENT_ID
string
required
Google OAuth 2.0 client ID from Google Cloud Console
GOOGLE_CLIENT_SECRET
string
required
Google OAuth 2.0 client secret from Google Cloud Console

Usage

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

// Redirect to Google OAuth
await signIn('google');

// With custom callback URL
await signIn('google', { 
  callbackUrl: '/dashboard' 
});

Credentials Provider

Email and password authentication backed by Firebase Authentication.

Request Parameters

email
string
required
User’s email addressExample: [email protected]
password
string
required
User’s password (minimum requirements defined by Firebase)Example: SecureP@ssw0rd

Sign In Request

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

const result = await signIn('credentials', {
  email: '[email protected]',
  password: 'SecureP@ssw0rd',
  redirect: false
});

if (result?.error) {
  // Handle error
  console.error(result.error);
} else {
  // Authentication successful
  console.log('Signed in successfully');
}

Authentication Flow

// Internal flow (from [...nextauth].ts:20-46)
async authorize(credentials) {
  // 1. Validate input
  if (!credentials?.email || !credentials?.password) {
    throw new Error("Email and password are required");
  }

  // 2. Authenticate with Firebase
  const userCredential = await signInWithEmailAndPassword(
    auth, 
    credentials.email, 
    credentials.password
  );
  const user = userCredential.user;

  // 3. Save/update user in Firestore
  const userRef = doc(db, "users", user.uid);
  await setDoc(userRef, {
    id: user.uid,
    email: user.email,
    name: user.displayName || "User",
  });

  // 4. Return user object for session
  return {
    id: user.uid,
    email: user.email,
    name: user.displayName || "User",
  };
}

Response

ok
boolean
Whether authentication was successful
error
string
Error message if authentication failedPossible values:
  • "Email and password are required"
  • "Invalid email or password"
status
number
HTTP status code
url
string
Redirect URL after authentication

NextAuth Endpoints

NextAuth.js automatically creates the following endpoints:

Sign In

GET /api/auth/signin
Displays the sign-in page (if using built-in UI) or redirects to provider.

Sign Out

GET /api/auth/signout
POST /api/auth/signout
Signs out the current user and clears the session.
import { signOut } from 'next-auth/react';

// Sign out and redirect to home
await signOut({ callbackUrl: '/' });

// Sign out without redirect
await signOut({ redirect: false });

Session

GET /api/auth/session
Returns the current session data.

Response

user
object
User information from Firestore
expires
string
ISO 8601 timestamp when the session expires

Example Response

{
  "user": {
    "id": "abc123xyz789",
    "email": "[email protected]",
    "name": "John Doe"
  },
  "expires": "2026-04-03T12:00:00.000Z"
}

Usage

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

export default function Component() {
  const { data: session, status } = useSession();
  
  if (status === 'loading') return <div>Loading...</div>;
  if (status === 'unauthenticated') return <div>Not signed in</div>;
  
  return <div>Signed in as {session.user.email}</div>;
}

CSRF Token

GET /api/auth/csrf
Returns a CSRF token for form submissions.

Response

{
  "csrfToken": "abc123..."
}

Providers

GET /api/auth/providers
Returns available authentication providers.

Response

{
  "google": {
    "id": "google",
    "name": "Google",
    "type": "oauth",
    "signinUrl": "/api/auth/signin/google",
    "callbackUrl": "/api/auth/callback/google"
  },
  "credentials": {
    "id": "credentials",
    "name": "Credentials",
    "type": "credentials",
    "signinUrl": "/api/auth/signin/credentials",
    "callbackUrl": "/api/auth/callback/credentials"
  }
}

Callbacks

JWT Callback

Called whenever a JWT is created or updated.
// From [...nextauth].ts:64-69
async jwt({ token, user }) {
  if (user) {
    token.sub = user.id; // Store user ID in token
  }
  return token;
}
token
object
The JWT token object
user
object
User object (only available on sign-in)

Session Callback

Called whenever a session is accessed (client or server).
// From [...nextauth].ts:50-63
async session({ session, token }) {
  if (token.sub) {
    // Fetch fresh user data from Firestore
    const userRef = doc(db, "users", token.sub);
    const userDoc = await getDoc(userRef);
    
    if (userDoc.exists()) {
      session.user = userDoc.data();
    }
  }
  return session;
}
session
object
The session object being created
token
object
The JWT token containing user ID
The session callback fetches user data from Firestore on every session check, ensuring the session always contains the latest user information.

Configuration

Session Strategy

session: { strategy: "jwt" }
The platform uses JWT-based sessions instead of database sessions for better scalability.
strategy
string
default:"jwt"
Session storage strategy. Can be "jwt" or "database".The platform uses JWT for stateless authentication.

Secret

secret: process.env.NEXTAUTH_SECRET
NEXTAUTH_SECRET
string
required
Secret key used to encrypt JWT tokens and sign cookies.Generate with: openssl rand -base64 32
The NEXTAUTH_SECRET must be set in production. Without it, NextAuth will throw an error.

Firestore Integration

User Document Creation

When a user signs in (via credentials or OAuth), their data is stored in Firestore:
// Collection: users
// Document ID: Firebase UID
const userRef = doc(db, "users", user.uid);
await setDoc(userRef, {
  id: user.uid,
  email: user.email,
  name: user.displayName || "User",
});

Session Data Retrieval

On each session access, user data is fetched from Firestore:
const userRef = doc(db, "users", token.sub);
const userDoc = await getDoc(userRef);

if (userDoc.exists()) {
  session.user = userDoc.data();
}
This ensures the session always contains the most up-to-date user information from Firestore, even if the profile was updated elsewhere.

Error Responses

Invalid Credentials

{
  "error": "Invalid email or password"
}
Returned when:
  • Email doesn’t exist in Firebase
  • Password is incorrect
  • Firebase authentication fails

Missing Fields

{
  "error": "Email and password are required"
}
Returned when the sign-in request is missing email or password.

Configuration Error

{
  "error": "Configuration"
}
Returned when:
  • Required environment variables are missing
  • Firebase credentials are invalid
  • NextAuth secret is not set (production only)

Session Error

{
  "error": "Unable to fetch user data from Firestore"
}
Returned when:
  • Firestore is unavailable
  • User document doesn’t exist
  • token.sub is undefined

Environment Variables

Complete list of required environment variables:
NEXTAUTH_SECRET
string
required
Secret for JWT encryption (generate with openssl rand -base64 32)
GOOGLE_CLIENT_ID
string
required
Google OAuth client ID from Google Cloud Console
GOOGLE_CLIENT_SECRET
string
required
Google OAuth client secret from Google Cloud Console
NEXT_PUBLIC_FIREBASE_API_KEY
string
required
Firebase API key for client-side authentication
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
string
required
Firebase auth domain (e.g., project-id.firebaseapp.com)
NEXT_PUBLIC_FIREBASE_PROJECT_ID
string
required
Firebase project ID
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
string
required
Firebase storage bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
string
required
Firebase messaging sender ID
NEXT_PUBLIC_FIREBASE_APP_ID
string
required
Firebase app ID
FIREBASE_CLIENT_EMAIL
string
required
Firebase Admin service account email
FIREBASE_PRIVATE_KEY
string
required
Firebase Admin service account private key

Example Implementation

Complete Sign-In Flow

import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/navigation';

export default function SignInPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const router = useRouter();

  const handleCredentialsSignIn = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    try {
      const result = await signIn('credentials', {
        email,
        password,
        redirect: false,
      });

      if (result?.error) {
        setError(result.error);
      } else if (result?.ok) {
        router.push('/dashboard');
      }
    } catch (err) {
      setError('An unexpected error occurred');
    } finally {
      setLoading(false);
    }
  };

  const handleGoogleSignIn = async () => {
    setLoading(true);
    await signIn('google', { callbackUrl: '/dashboard' });
  };

  return (
    <div>
      <h1>Sign In</h1>
      
      {error && <div className="error">{error}</div>}
      
      <form onSubmit={handleCredentialsSignIn}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          required
        />
        <button type="submit" disabled={loading}>
          {loading ? 'Signing in...' : 'Sign In'}
        </button>
      </form>
      
      <button onClick={handleGoogleSignIn} disabled={loading}>
        Sign in with Google
      </button>
    </div>
  );
}

Protected API Route

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

export async function GET(req: NextRequest) {
  // Validate session
  const session = await getServerSession(authOptions);
  
  if (!session) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }
  
  // Access user data
  const userId = session.user.id;
  const userEmail = session.user.email;
  
  // Proceed with authenticated request
  return NextResponse.json({
    message: 'Protected data',
    userId,
    userEmail,
  });
}

Next Steps

Authentication Overview

Learn about the authentication flow and strategy

User Profile

Fetch user profile data

User Onboarding

Complete user onboarding

Authentication Guide

Complete authentication documentation

Build docs developers (and LLMs) love