Skip to main content

Command Palette

Search for a command to run...

Guide to NextAuth: Stateless(default) and Stateful

Published
4 min read

Prerequisites

  • Solid understanding of Authentication fundamentals

  • Familiarity with JWT, Sessions, OAuth

If you want to read about above topics checkout this article https://noncodersuccess.medium.com/understanding-session-jwt-token-sso-and-oauth-2-0-c166b843fb1c


This article does not focus on exact configurations or line-by-line code — those are well documented and easy to find with a quick search. But rather we will be looking into the High level working and the different ways to integrate it based on our requirements.

Stateless vs Stateful — What Do We Mean?

Stateless Authentication

  • No persistent session data stored on the server

  • All required identity data is embedded inside a JWT

  • Server validates the JWT

Source of truth: JWT (identity) + Database (authorization)

Stateful Authentication

  • Session information is stored on the server

  • Client only holds a session identifier

  • Server fetches session data on every request

  • Requires shared storage (DB / Redis) for scaling

Source of truth: Server-side session store

1. Credential Verification

User Logins by Username & password or OAuth(link to OAuth Article)

Users can authenticate using any method like:

  • Username & Password

  • OAuth providers (Google, GitHub, etc.)

We will be use the example of OAuth

1.1 User initiates login

User clicks “Sign in with Google”

1.2 OAuth authentication (external)

  • Browser → Google OAuth

  • User authenticates & consents

  • Google redirects back with authorization_code

Google confirms identity

I also has a article on OAuth, if you want you can read here : https://rahul-choudhary.hashnode.dev/login-with-google-a-beginners-guide-to-oauth

1.3 User & Account resolution (Prisma Adapter)

NextAuth:

  • Finds or creates a User

  • Links OAuth identity in Account

User ← Account (google)

Identity is now internalized.

2. Session Creation

This is where Stateless vs Stateful diverges.

2.1 Stateless (Default in NextAuth)

  • NextAuth creates a JWT

  • JWT contains:

    • sub (user id)

    • email, name

    • custom claims (optional)

    • exp, iat

  • JWT is:

    • Signed

    • Encrypted

    • Stored in an HTTP-only cookie

No database entry is created for the session.

2.2 Stateful Sessions

  • A session record is created in the database

  • Cookie contains only a sessionId

Cookie → sessionId → DBsession →User

This requires:

session: {
strategy:"database"
}

JWT session is created

This JWT is set in the cookie

3. Request Handling

For every protected request, NextAuth does:

  1. Reads cookie

  2. Extracts token (JWT or sessionId)

  3. Validates it

  4. Resolves user identity


Server Component

const session =awaitauth()

Client Component

const {data: session } =useSession()

API Route / Server Action

const session =awaitauth()

If session === null → user is unauthenticated.


4. Authorization (Your Responsibility)

Authentication ≠ Authorization.

After auth(), you decide what the user can do.

Two Common Approaches

Option 1: Store role in JWT

  • Faster

  • Risk of stale roles

const session =awaitauth()

const user =await db.user.findUnique({
where: {id: session.user.id }
})

if (user.role !=="ADMIN") {
thrownewForbiddenError()
}

Database is the source of truth

JWT is only proof of identity, not authority.


5. JWT Expiry (Stateless Boundary)

Every JWT has an exp claim.

Once expired:

JWT is no longertrusted

Requests with expired JWT:

  • Cannot access protected resources

  • May still be eligible for renewal


5.1 Silent JWT Refresh (Stateless Renewal)

This happens automatically on the server.

Flow:

  1. Request arrives with expired JWT

  2. NextAuth detects expiry

  3. jwt callback executes

  4. Server checks:

    • Is session within maxAge?

    • Does user still exist?

If YES

  • New JWT generated

  • Cookie updated

  • Request continues

If NO

  • Session rejected

  • User redirected to login

No refresh token

Client does nothing


5.2 Absolute Session Limit

Example:

JWT expiry:10minutes
Session maxAge:30days

After 30 days:

  • No silent refresh

  • Forced re-login

This prevents infinite sessions.


6. Logout

6.1 Logout in Stateless Mode

Logout simply:

Deletes the JWT cookie

That’s it.

No server cleanup required.


6.2 Logout in Stateful Mode

Logout does two things:

  1. Deletes cookie

  2. Deletes session record from DB

import { prisma } from "@/lib/prisma"

await prisma.session.delete({
  where: {
    id: sessionId
  }
})

This allows:

  • Immediate session invalidation

  • Central session control


When to Use Stateless Sessions

Best for:

  • Most Next.js apps

  • High scalability

  • Serverless deployments

  • Simple auth requirements

Pros

  • No session storage

  • Easy horizontal scaling

  • Fast request handling

Cons

  • Harder to revoke instantly

  • JWT size grows with claims


When to Use Stateful Sessions

Best for:

  • Enterprise apps

  • Strict security requirements

  • Admin dashboards

  • Compliance-heavy systems

Pros

  • Immediate revocation

  • Centralized session control

  • Smaller cookies

Cons

  • Requires DB / Redis

  • Scaling complexity

  • Slightly slower per request