3 BACKEND API

JWT Strategy & Token Rotation

🎫 The JWT Strategy: Stateless Authentication

In the previous lesson, we learned how to safely hash a password and confirm a user's login. But what happens on the next request? When the user clicks "View My Profile", HTTP is mathematically Stateless. The server immediately forgets who the user is the second the login request ends.

To allow users to stay logged in, we use JSON Web Tokens (JWT).


1️⃣ What is a JWT?

A JWT (pronounced "jot") is a digital passport. When a user logs in successfully, the Express server cryptographically signs a passport and hands it to the React frontend. For every future request, React simply flashes the passport to the server.

A JWT is composed of three strings separated by dots: Header.Payload.Signature

eyJhbGciOiJIUzI1Ni... . eyJ1c2VySWQiOjl9... . SflKxwRJSMeKKF2QT...
  1. Header: Tells the server what algorithm was used (usually HMAC SHA256).
  2. Payload: The actual JSON data (e.g. { "userId": 9, "role": "admin" }). This is Base64 Encoded, NOT ENCRYPTED! Anyone with an internet connection can read the payload. Never put passwords in here.
  3. Signature: The magic security seal. The server takes the Header, Payload, and a secret 50-character password hidden in your .env file, and hashes them together. If a hacker tries to modify their userId from 9 to 1 in the Payload, the mathematical Signature breaks instantly, and the server rejects it.

2️⃣ Generating The Token (The Login Route)

npm install jsonwebtoken
import jwt from 'jsonwebtoken';
import express from 'express';

const app = express();

app.post('/api/login', async (req, res) => {
    // 1. Verify password (covered in previous lesson)
    const user = await verifyPassword(req.body.username, req.body.password);
    if (!user) return res.status(401).send();

    // 2. Generate the Token
    // We embed non-sensitive data into the Payload so we don't have to query the 
    // database on every single future request!
    const payload = {
        userId: user.id,
        role: user.role
    };

    // 3. Sign it with the ultra-secret Server Key
    const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, {
        expiresIn: '2h' // Security: Force the token to self-destruct in 2 hours.
    });

    // 4. Send the passport to React
    res.json({ message: "Logged in", token: token });
});

3️⃣ Verifying The Token (The Auth Middleware)

Now that React has the token, it will attach it to the Authorization header of every fetch() request it makes.

In Express, we build a Global Middleware Shield that intercepts incoming traffic, looks at the passport, verifies the cryptographic signature, and explicitly allows or denies the request.

const requireAuth = (req, res, next) => {
    // 1. Extract the token from the "Authorization: Bearer <token>" header
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: "Missing Passport" });
    }

    const token = authHeader.split(' ')[1];

    try {
        // 2. Mathematically verify the signature using our Server Key
        // If the token is modified or expired, this function THROWS A FATAL ERROR.
        const decodedPayload = jwt.verify(token, process.env.JWT_SECRET_KEY);

        // 3. Attach the decoded user data directly to the Express Request object!
        // Now, every downstream route automatically knows exactly who the user is!
        req.user = decodedPayload;

        // 4. Everything is safe. Proceed to the actual Route.
        next();
    } catch (err) {
        // Token was fake or expired
        return res.status(401).json({ error: "Invalid or Expired Passport" });
    }
};

// --- Utilizing the Shield ---

app.get('/api/dashboard', requireAuth, (req, res) => {
    // We magically know their ID because the middleware attached it!
    res.send(`Welcome to your private dashboard, User #${req.user.userId}`);
});

4️⃣ Storing the Token in React

Where does React keep the token string between page refreshes?

1. LocalStorage (Easy but Vulnerable) If you use localStorage.setItem('token', token), it survives page refreshes. However, it is vulnerable to XSS (Cross-Site Scripting). If a hacker injects malicious JavaScript into your site, they can easily read localStorage and steal the token.

2. HttpOnly Cookies (Enterprise Standard) Instead of sending the token back as a JSON string, Express can force the token into a deeply secure Browser Cookie.

// Express sending an HttpOnly Cookie
res.cookie('token', token, {
    httpOnly: true, // Javascript is physically banned from reading this cookie. Immune to XSS!
    secure: true,   // Only send over HTTPS encryption.
    sameSite: 'strict', // Prevents CSRF attacks
    maxAge: 7200000 // 2 hours in ms
});

When using HttpOnly Cookies, the browser automatically attaches the passport to every single outgoing HTTP request. Your React code doesn't even need to know the token exists!


💡 Summary Lexicon

ConceptExplanationRule of Thumb
StatelessnessServer instantly forgets users after a response.Need JWTs to fake long-term memory.
PayloadThe middle section of a JWT.NEVER put a password or credit card here. It is plain text.
SignatureThe cryptographic seal.If the Payload is altered, the Signature mathematically breaks.
Middlewarereq.user = payloadThe middleware cracks open the token, and attaches the user to the req object for the final route.
HttpOnly CookieThe safest storage location.Prevents XSS script attacks from stealing passports.

Knowledge Check

Complete this quick quiz to verify your understanding and unlock the next module.