In this section, we will explore how to implement JSON Web Token (JWT) authentication in a Next.js application. JWT is a compact, URL-safe means of representing claims to be transferred between two parties. It allows you to authenticate users and protect routes in your application.
Before diving into implementation, let's understand the basics of JWT:
First, ensure you have a Next.js project set up. If not, create one using:
npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
We will use jsonwebtoken for signing and verifying tokens, and express-session to manage sessions.
npm install jsonwebtoken express-session
Create a utility file to handle JWT operations. This will include generating and verifying tokens.
// utils/jwt.js
import jwt from 'jsonwebtoken';
const secretKey = process.env.JWT_SECRET || 'your_secret_key'; // Use environment variables for security
export const generateToken = (payload) => {
return jwt.sign(payload, secretKey, { expiresIn: '1h' }); // Token expires in 1 hour
};
export const verifyToken = (token) => {
try {
return jwt.verify(token, secretKey);
} catch (error) {
throw new Error('Invalid token');
}
};
Create a middleware to protect routes that require authentication.
// middleware/auth.js
import { verifyToken } from '../utils/jwt';
export default function authMiddleware(handler) {
return async (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
try {
const decoded = verifyToken(token);
req.user = decoded;
return handler(req, res);
} catch (error) {
return res.status(403).json({ message: error.message });
}
};
}
Use the middleware to protect routes. For example, let's protect a profile route.
// pages/api/profile.js
import authMiddleware from '../../middleware/auth';
export default authMiddleware((req, res) => {
if (req.method === 'GET') {
return res.status(200).json({ message: 'Profile accessed', user: req.user });
}
});
Implement a login endpoint to authenticate users and issue JWTs.
// pages/api/login.js
import { generateToken } from '../../utils/jwt';
export default async function handler(req, res) {
if (req.method === 'POST') {
const { username, password } = req.body;
// Normally you would validate credentials against a database
if (username === 'admin' && password === 'password') {
const token = generateToken({ username });
return res.status(200).json({ message: 'Login successful', token });
}
return res.status(401).json({ message: 'Invalid credentials' });
}
}
On the client side, you can use the token to make authenticated requests. Here's an example using fetch.
// pages/index.js
import { useState } from 'react';
export default function Home() {
const [token, setToken] = useState('');
const login = async () => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'password' }),
});
if (response.ok) {
const data = await response.json();
setToken(data.token);
} else {
console.error('Login failed');
}
};
const fetchProfile = async () => {
const response = await fetch('/api/profile', {
headers: { Authorization: `Bearer ${token}` },
});
if (response.ok) {
const data = await response.json();
console.log(data);
} else {
console.error('Failed to fetch profile');
}
};
return (
<div>
<button onClick={login}>Login</button>
{token && <button onClick={fetchProfile}>Fetch Profile</button>}
</div>
);
}
JWT authentication is a robust method for securing your Next.js application. By following the steps outlined in this tutorial, you can implement JWT-based authentication, protect routes, and manage user sessions effectively. Remember to adhere to best practices to ensure the security of your application.