JWT Authentication — Complete Implementation

Sanjeev SharmaSanjeev Sharma
4 min read

Advertisement

JWT (JSON Web Tokens) are standard for stateless authentication. Perfect for APIs and distributed systems.

JWT Basics

A JWT has three parts: header.payload.signature

import jwt from "jsonwebtoken";

const secret = process.env.JWT_SECRET || "your-secret";

// Generate token
const token = jwt.sign(
  { userId: 123, email: "user@example.com" },
  secret,
  { expiresIn: "24h" }
);

// Verify token
const decoded = jwt.verify(token, secret);
console.log(decoded); // { userId: 123, email: '...', iat, exp }

Complete Authentication Flow

// Register
app.post("/register", async (req, res) => {
  const { email, password } = req.body;

  const hashed = await bcrypt.hash(password, 10);
  const user = await db.users.create({ email, password: hashed });

  const token = jwt.sign({ userId: user.id }, secret, { expiresIn: "24h" });
  res.json({ token });
});

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

  const user = await db.users.findOne({ email });
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ error: "Invalid credentials" });
  }

  const token = jwt.sign({ userId: user.id }, secret, { expiresIn: "24h" });
  res.json({ token });
});

// Protected route
app.get("/me", authMiddleware, (req: any, res) => {
  res.json({ userId: req.userId });
});

// Middleware
const authMiddleware = (req: any, res: any, next: any) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "No token" });

  try {
    const decoded = jwt.verify(token, secret) as any;
    req.userId = decoded.userId;
    next();
  } catch {
    res.status(401).json({ error: "Invalid token" });
  }
};

Refresh Tokens

// Generate tokens
function generateTokens(userId: number) {
  const accessToken = jwt.sign({ userId }, secret, { expiresIn: "15m" });
  const refreshToken = jwt.sign({ userId }, refreshSecret, { expiresIn: "7d" });
  return { accessToken, refreshToken };
}

// Refresh endpoint
app.post("/refresh", (req, res) => {
  const { refreshToken } = req.body;

  try {
    const decoded = jwt.verify(refreshToken, refreshSecret) as any;
    const tokens = generateTokens(decoded.userId);
    res.json(tokens);
  } catch {
    res.status(401).json({ error: "Invalid refresh token" });
  }
});

Best Practices

const config = {
  // Use strong secret
  secret: process.env.JWT_SECRET,
  // Short expiration for security
  accessTokenExpiry: "15m",
  // Longer for refresh token
  refreshTokenExpiry: "7d",
  // Sign with algorithm
  algorithm: "HS256" as const,
};

// Token payload (minimize data)
interface TokenPayload {
  userId: number;
  email?: string; // Optional, avoid large payloads
}

// Store refresh tokens in database
app.post("/logout", authMiddleware, async (req: any, res) => {
  await db.refreshTokens.delete(req.userId);
  res.json({ ok: true });
});

Securing JWTs

  1. HTTPS only - tokens in transport must be encrypted
  2. httpOnly cookies - store in httpOnly cookies, not localStorage
  3. CORS properly - restrict cross-origin requests
  4. Short expiration - access tokens 15-60 minutes
  5. Revocation - implement token blacklist for logout
// Store in httpOnly cookie
app.post("/login", async (req, res) => {
  const { token } = generateTokens(user.id);
  res.cookie("token", token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "strict",
    maxAge: 15 * 60 * 1000, // 15 minutes
  });
  res.json({ ok: true });
});

// Read from cookie
app.use((req: any, res, next) => {
  const token = req.cookies.token || req.headers.authorization?.split(" ")[1];
  if (token) {
    try {
      req.user = jwt.verify(token, secret);
    } catch {}
  }
  next();
});

FAQ

Q: Should I store user data in JWT? A: Minimize data. Only store userId. Fetch other data from database.

Q: What if I need to update user permissions? A: Use short-lived tokens. Re-authenticate on permission changes.

Q: How do I handle token expiration gracefully? A: Use refresh tokens. Access token expires, use refresh to get new access token.


JWT is the standard for modern API authentication. Implement it correctly and you have a secure, scalable system.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro