How to Use JWT with Next.js for Authentication
To use
JWT with Next.js, generate a token on user login using a library like jsonwebtoken, then send it to the client via cookies or headers. On protected API routes or pages, verify the token to authenticate users securely.Syntax
This is the basic pattern for using JWT in Next.js:
jsonwebtoken.sign(payload, secret, options): Creates a JWT token with user data.jsonwebtoken.verify(token, secret): Checks if the token is valid and extracts the payload.- Use API routes in Next.js to handle login and token verification.
- Store the token securely in HTTP-only cookies or local storage on the client.
javascript
import jwt from 'jsonwebtoken'; // Create a token const token = jwt.sign({ userId: 123 }, process.env.JWT_SECRET, { expiresIn: '1h' }); // Verify a token try { const decoded = jwt.verify(token, process.env.JWT_SECRET); console.log(decoded.userId); } catch (error) { console.error('Invalid token'); }
Output
123
Example
This example shows a simple Next.js API route for login that issues a JWT token and a protected API route that verifies the token.
javascript
import jwt from 'jsonwebtoken'; import { serialize } from 'cookie'; const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; // API route: /api/login export default async function loginHandler(req, res) { if (req.method !== 'POST') { return res.status(405).end(); } const { username, password } = req.body; // Dummy user check if (username === 'user' && password === 'pass') { const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '1h' }); // Set token in HTTP-only cookie res.setHeader('Set-Cookie', serialize('token', token, { httpOnly: true, path: '/', maxAge: 3600, sameSite: 'lax', secure: process.env.NODE_ENV === 'production' })); return res.status(200).json({ message: 'Logged in' }); } res.status(401).json({ message: 'Invalid credentials' }); } // API route: /api/protected export default async function protectedHandler(req, res) { const { token } = req.cookies; if (!token) { return res.status(401).json({ message: 'No token provided' }); } try { const decoded = jwt.verify(token, JWT_SECRET); return res.status(200).json({ message: 'Protected data', user: decoded.username }); } catch (error) { return res.status(401).json({ message: 'Invalid token' }); } }
Output
{"message":"Logged in"} (on login) and {"message":"Protected data","user":"user"} (on protected route with valid token)
Common Pitfalls
- Not using HTTP-only cookies to store JWT can expose tokens to XSS attacks.
- Forgetting to verify the token on protected routes leads to security holes.
- Using a weak or hardcoded secret key reduces token security.
- Not handling token expiration causes unexpected logout or errors.
- Sending JWT in local storage without secure flags can expose tokens to theft.
javascript
/* Wrong: Storing token in localStorage without secure flags */ localStorage.setItem('token', token); /* Right: Store token in HTTP-only cookie on server side */ res.setHeader('Set-Cookie', serialize('token', token, { httpOnly: true, secure: true, path: '/' }));
Quick Reference
| Step | Action | Notes |
|---|---|---|
| 1 | Generate JWT on login | Use jsonwebtoken.sign with a strong secret |
| 2 | Send JWT to client | Use HTTP-only cookies for security |
| 3 | Verify JWT on protected routes | Use jsonwebtoken.verify to check token |
| 4 | Handle token expiration | Refresh or force re-login as needed |
| 5 | Secure your secret key | Keep it in environment variables |
Key Takeaways
Always generate and verify JWT tokens on the server side using a strong secret.
Store JWT tokens in HTTP-only cookies to protect against client-side attacks.
Verify JWT tokens on every protected API route or page to ensure user authentication.
Handle token expiration gracefully to maintain user experience and security.
Never expose your JWT secret key in client-side code or public repositories.