Understanding JWT Authentication in Next.js 14 App Router
JWT authentication is a common way to protect APIs and user sessions in modern web apps. In this focused guide, you will learn how Next.js JWT flows work inside the App Router, how to issue and verify tokens, and where this fits into Next.js 14 authentication. This JSON Web Token tutorial keeps the setup simple while highlighting practical Next.js security considerations.
What JWT Authentication Does
A JSON Web Token is a signed string that contains user-related claims, such as a user ID or role. After login, the server creates a token and sends it to the client, often through an HTTP-only cookie. On later requests, the server verifies the token before returning protected data.
In Next.js 14 App Router, this usually means:
1. Login Route
A route handler validates credentials and signs a JWT.
2. Cookie Storage
The token is stored in a secure, HTTP-only cookie to reduce XSS exposure.
3. Protected Routes
Server components, route handlers, or middleware verify the token before allowing access.
Install a JWT Library
A popular choice is jose, which works well in modern Next.js environments.
npm install joseCreate a JWT Helper
Keep signing and verification logic in one reusable file.
import { SignJWT, jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET)
export async function signToken(payload: { userId: string }) {
return await new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('1d')
.sign(secret)
}
export async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, secret)
return payload
}Add a Login Route Handler
In the App Router, route handlers live inside the app/api folder. After verifying the user's credentials, sign a token and set it in a cookie.
import { NextResponse } from 'next/server'
import { signToken } from '@/lib/jwt'
export async function POST() {
const token = await signToken({ userId: '123' })
const response = NextResponse.json({ success: true })
response.cookies.set('token', token, { httpOnly: true, secure: true, path: '/' })
return response
}Read the Token in a Protected Route
You can access cookies directly in server-side code and verify the token before responding.
import { cookies } from 'next/headers'
import { verifyToken } from '@/lib/jwt'
export async function GET() {
const token = cookies().get('token')?.value
if (!token) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const payload = await verifyToken(token)
return Response.json({ user: payload })
}Where Middleware Fits
Middleware can check for a token before a user reaches protected pages. This is useful for redirects, but avoid putting too much auth logic there. Heavy verification is often better handled in route handlers or server components.
Next.js Security Best Practices
For solid Next.js security, follow a few essentials:
Use HTTP-only Cookies
Avoid storing JWTs in localStorage when possible. HTTP-only cookies are safer against client-side script access.
Set Expiration Times
Short-lived tokens reduce risk if a token is leaked.
Keep Secrets in Environment Variables
Store JWT_SECRET in .env, never in source code.
Validate Users Before Signing
Always verify credentials from your database before issuing a token.
Final Thoughts
This approach gives you a clean starting point for Next.js JWT workflows in the App Router. For many apps, JWT-based cookies are enough to handle session-like behavior while keeping your Next.js 14 authentication flow straightforward. As your app grows, you can add refresh tokens, role-based access control, and stronger monitoring. If you want a compact JSON Web Token tutorial for modern Next.js apps, this pattern is a practical place to begin.