# Next.js 15 Handle JWT from Backend If your **Next.js 15 frontend** receives a **JWT token from the backend**, you can handle it securely using **middleware** and **cookies**. Here's how you can process and store the token when it's passed from the backend. --- ## **📌 Steps to Handle JWT from Backend in Next.js Middleware** 1. **Receive the JWT token** from the backend API response. 2. **Store the token in an HTTP-only cookie** for security. 3. **Use middleware** to validate the token and protect routes. 4. **Send the token with authenticated requests.** --- ## **1️⃣ Fetch Token from Backend and Store in Cookie** When a user logs in, your Next.js app will send credentials to the backend and store the received **JWT token** in an **HTTP-only** cookie. ```tsx // src/app/login/page.tsx "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; export default function LoginPage() { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const router = useRouter(); async function handleLogin(e: React.FormEvent) { e.preventDefault(); const res = await fetch("https://your-backend.com/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), }); const data = await res.json(); if (res.ok && data.token) { // Store JWT token in an HTTP-only cookie via Next.js API route await fetch("/api/auth/store-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token: data.token }), }); router.push("/dashboard"); } else { alert("Login failed"); } } return ( <form onSubmit={handleLogin}> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" required /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" required /> <button type="submit">Login</button> </form> ); } ``` --- ## **2️⃣ Store Token in Secure Cookie (API Route)** Since the frontend cannot set **HTTP-only** cookies directly, we use a Next.js **API route** to store the JWT in a secure cookie. ```ts // src/app/api/auth/store-token/route.ts import { NextResponse } from "next/server"; import { serialize } from "cookie"; export async function POST(req: Request) { const { token } = await req.json(); if (!token) { return NextResponse.json({ error: "Token is required" }, { status: 400 }); } const response = NextResponse.json({ message: "Token stored" }); response.headers.append( "Set-Cookie", serialize("token", token, { httpOnly: true, // Prevents JavaScript access (XSS protection) secure: process.env.NODE_ENV === "production", // Use HTTPS in production sameSite: "strict", // Prevent CSRF attacks path: "/", // Cookie available for all routes }) ); return response; } ``` --- ## **3️⃣ Middleware to Protect Routes** Use **Next.js Middleware** to check if the JWT exists and is valid. ```ts // src/middleware.ts import { NextResponse } from "next/server"; import { jwtVerify } from "jose"; const SECRET_KEY = new TextEncoder().encode("your-secret-key"); export async function middleware(req: Request) { const token = req.headers.get("cookie")?.split("token=")[1]?.split(";")[0]; if (!token) { return NextResponse.redirect(new URL("/login", req.url)); } try { await jwtVerify(token, SECRET_KEY); return NextResponse.next(); } catch (error) { return NextResponse.redirect(new URL("/login", req.url)); } } // Apply middleware only to protected routes export const config = { matcher: ["/dashboard/:path*"], }; ``` --- ## **4️⃣ Logout and Clear Token** To log out, we remove the token by expiring the cookie. ```ts // src/app/api/auth/logout/route.ts import { NextResponse } from "next/server"; import { serialize } from "cookie"; export async function GET() { const response = NextResponse.json({ message: "Logged out" }); response.headers.append( "Set-Cookie", serialize("token", "", { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict", path: "/", expires: new Date(0), // Expire the cookie }) ); return response; } ``` ### **Client-side Logout Function** ```tsx // Logout button async function handleLogout() { await fetch("/api/auth/logout"); window.location.href = "/login"; } ``` --- ## **5️⃣ Fetch User from Backend Using Token** Create an API route to fetch user details from the backend using the stored token. ```ts // src/app/api/auth/me/route.ts import { NextResponse } from "next/server"; export async function GET(req: Request) { const token = req.headers.get("cookie")?.split("token=")[1]?.split(";")[0]; if (!token) { return NextResponse.json({ error: "Not authenticated" }, { status: 401 }); } const res = await fetch("https://your-backend.com/api/me", { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { return NextResponse.json({ error: "Invalid token" }, { status: 401 }); } const user = await res.json(); return NextResponse.json(user); } ``` --- ## **6️⃣ Fetch User on Client Side** Fetch user data on the client using the `/api/auth/me` route. ```tsx "use client"; import { useEffect, useState } from "react"; export default function Dashboard() { const [user, setUser] = useState(null); useEffect(() => { fetch("/api/auth/me") .then((res) => res.json()) .then((data) => setUser(data)) .catch(() => setUser(null)); }, []); return ( <div> <h1>Dashboard</h1> {user ? <p>Welcome, {user.username}!</p> : <p>Loading...</p>} </div> ); } ``` --- ## **✅ Summary** | **Step** | **Action** | |----------|-----------| | 1️⃣ | **Receive JWT token from backend** after login | | 2️⃣ | **Store token securely in an HTTP-only cookie** via an API route | | 3️⃣ | **Use middleware** to validate the token & protect routes | | 4️⃣ | **Logout by clearing the token cookie** | | 5️⃣ | **Send the token with requests to the backend** | | 6️⃣ | **Fetch user details in a secure way** |