Master Code
On The Go

Learn. Practice. Build.

Home › How-To › Node.js JWT Authentication

Node.js Guide

Node.js JWT Authentication

Secure your Node.js API with JSON Web Tokens. Sign, verify, and protect routes with production-ready middleware.

Quick answer: Install jsonwebtoken. Sign tokens with jwt.sign(payload, secret, { expiresIn: '1h' }). Verify with jwt.verify(token, secret). Store tokens in HTTP-only cookies, never in localStorage.

1

Install jsonwebtoken

npm install jsonwebtoken

Store your JWT secret in .env:

JWT_SECRET=your-long-random-secret
JWT_EXPIRES_IN=1h

Generate a strong secret with:

node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
2

Sign a Token on Login

import jwt from 'jsonwebtoken';

app.post('/login', async (req, res) => {
    const { email, password } = req.body;

    // 1. Find user by email
    const user = await User.findOne({ email });
    if (!user) return res.status(401).json({ error: 'Invalid credentials' });

    // 2. Compare password (bcrypt)
    const valid = await bcrypt.compare(password, user.password);
    if (!valid) return res.status(401).json({ error: 'Invalid credentials' });

    // 3. Sign JWT
    const token = jwt.sign(
        { userId: user.id, email: user.email },
        process.env.JWT_SECRET,
        { expiresIn: process.env.JWT_EXPIRES_IN }
    );

    // 4. Return in HTTP-only cookie
    res.cookie('token', token, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 3600000  // 1 hour
    });

    res.json({ message: 'Logged in' });
});
3

Verify Middleware

export function authenticate(req, res, next) {
    // Read token from cookie or Authorization header
    const token =
        req.cookies?.token ||
        req.headers.authorization?.replace('Bearer ', '');

    if (!token) {
        return res.status(401).json({ error: 'No token provided' });
    }

    try {
        const payload = jwt.verify(token, process.env.JWT_SECRET);
        req.user = payload;   // attach to request
        next();
    } catch (err) {
        return res.status(403).json({ error: 'Invalid or expired token' });
    }
}
4

Protect Routes

import { authenticate } from './middleware/auth.js';

// Public route
app.get('/posts', (req, res) => {
    res.json({ posts: [] });
});

// Protected route
app.post('/posts', authenticate, (req, res) => {
    console.log('User:', req.user.userId);
    // Create the post...
    res.status(201).json({ message: 'Created' });
});

// Admin-only route
app.delete('/posts/:id', authenticate, requireAdmin, (req, res) => {
    // ...
});
5

Refresh Tokens (Long Sessions)

Access tokens are short-lived. Issue a separate refresh token to get new ones:

// On login — issue both
const accessToken = jwt.sign(
    { userId: user.id },
    process.env.JWT_SECRET,
    { expiresIn: '15m' }
);

const refreshToken = jwt.sign(
    { userId: user.id },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: '7d' }
);

// Store refresh token in httpOnly cookie
res.cookie('refresh', refreshToken, { httpOnly: true, secure: true });

// Endpoint to refresh
app.post('/refresh', (req, res) => {
    const token = req.cookies.refresh;
    if (!token) return res.status(401).end();

    try {
        const { userId } = jwt.verify(token, process.env.JWT_REFRESH_SECRET);
        const newAccess = jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '15m' });
        res.json({ accessToken: newAccess });
    } catch {
        res.status(403).end();
    }
});

🛡️ Security Best Practices

Never store JWTs in localStorage. localStorage is accessible from any script on the page. An XSS attack steals every token instantly. Use HTTP-only cookies with Secure and SameSite=Strict.

❓ Frequently Asked Questions

What is JWT in Node.js?;

A compact, self-contained way to transmit authentication info as a JSON object. Standard for stateless APIs.

Should I store JWTs in localStorage?

No. Use HTTP-only cookies with Secure and SameSite flags. localStorage is vulnerable to XSS.

How long should a JWT last?

15 minutes to 1 hour for access tokens. Use refresh tokens for longer sessions.

Can JWTs be revoked?

Not easily — they're stateless. Use short expiry + refresh tokens, or a blacklist in Redis.

Where should the JWT secret go?

In a .env file for local dev, and in the host's environment variables for production. Never commit it to Git.

🎯 What's Next?

← All How-To Guides