Endpoint
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 OAuth 2.0 client ID from Google Cloud Console
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
User’s password (minimum requirements defined by Firebase) Example: SecureP@ssw0rd
Sign In Request
Client-Side
Direct API Call
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
Whether authentication was successful
Error message if authentication failed Possible values:
"Email and password are required"
"Invalid email or password"
Redirect URL after authentication
NextAuth Endpoints
NextAuth.js automatically creates the following endpoints:
Sign In
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.
Client-Side
Direct API Call
import { signOut } from 'next-auth/react' ;
// Sign out and redirect to home
await signOut ({ callbackUrl: '/' });
// Sign out without redirect
await signOut ({ redirect: false });
Session
Returns the current session data.
Response
User information from Firestore
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
Client-Side Hook
Direct Fetch
Server-Side
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
Returns a CSRF token for form submissions.
Response
{
"csrfToken" : "abc123..."
}
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 ;
}
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 ;
}
The session object being created
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.
Session storage strategy. Can be "jwt" or "database". The platform uses JWT for stateless authentication.
Secret
secret : process . env . NEXTAUTH_SECRET
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:
Secret for JWT encryption (generate with openssl rand -base64 32)
Google OAuth client ID from Google Cloud Console
Google OAuth client secret from Google Cloud Console
NEXT_PUBLIC_FIREBASE_API_KEY
Firebase API key for client-side authentication
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
Firebase auth domain (e.g., project-id.firebaseapp.com)
NEXT_PUBLIC_FIREBASE_PROJECT_ID
Firebase project ID
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
Firebase storage bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
Firebase messaging sender ID
NEXT_PUBLIC_FIREBASE_APP_ID
Firebase app ID
Firebase Admin service account email
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