0
0
NextjsHow-ToBeginner · 4 min read

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

StepActionNotes
1Generate JWT on loginUse jsonwebtoken.sign with a strong secret
2Send JWT to clientUse HTTP-only cookies for security
3Verify JWT on protected routesUse jsonwebtoken.verify to check token
4Handle token expirationRefresh or force re-login as needed
5Secure your secret keyKeep 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.