Skip to main content
Practice Problems

What is JWT and how does it work?

JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. It's commonly used for authentication and information exchange.

JWT Structure

A JWT consists of three parts separated by dots (.):

``` xxxxx.yyyyy.zzzzz ```

1. Header

Contains token type and signing algorithm:

```json { "alg": "HS256", "typ": "JWT" } ```

2. Payload

Contains claims (user data):

```json { "sub": "1234567890", "name": "John Doe", "email": "john@example.com", "iat": 1516239022, "exp": 1516242622 } ```

3. Signature

Ensures token wasn't tampered with:

```javascript HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) ```

Complete Example

``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ```

How Authentication Works

1. User Login

```javascript // Backend (Node.js/Express) app.post('/api/login', async (req, res) => { const { email, password } = req.body;

// Verify credentials const user = await User.findByEmail(email); if (!user || !await user.checkPassword(password)) { return res.status(401).json({ error: 'Invalid credentials' }); }

// Generate JWT const jwt = require('jsonwebtoken'); const token = jwt.sign( { userId: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET, { expiresIn: '24h' } );

res.json({ token }); }); ```

2. Client Stores Token

```javascript // Frontend const login = async (email, password) => { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) });

const { token } = await response.json();

// Store in localStorage localStorage.setItem('token', token); // Or in memory for better security // sessionStorage.setItem('token', token); }; ```

3. Client Sends Token

```javascript // Include token in requests const getData = async () => { const token = localStorage.getItem('token');

const response = await fetch('/api/protected', { headers: { 'Authorization': `Bearer ${token}` } });

return response.json(); }; ```

4. Server Verifies Token

```javascript // Middleware to verify JWT const jwt = require('jsonwebtoken');

const verifyToken = (req, res, next) => { const authHeader = req.headers.authorization;

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

const token = authHeader.substring(7);

try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } };

// Protected route app.get('/api/protected', verifyToken, (req, res) => { res.json({ data: 'Secret data', user: req.user }); }); ```

JWT Claims

Standard Claims (Registered)

  • `iss` (issuer): Token issuer
  • `sub` (subject): User ID
  • `aud` (audience): Intended recipient
  • `exp` (expiration): Expiration time
  • `iat` (issued at): Time token was created
  • `nbf` (not before): Token not valid before this time

```javascript const token = jwt.sign( { iss: 'myapp.com', sub: '123', aud: 'myapp-api', exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour iat: Math.floor(Date.now() / 1000) }, secret ); ```

Best Practices

1. Short Expiration Times

```javascript // Access token: short-lived const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' });

// Refresh token: long-lived, more secure const refreshToken = jwt.sign(payload, refreshSecret, { expiresIn: '7d' }); ```

2. Secure Storage

```javascript // ❌ Bad: localStorage vulnerable to XSS localStorage.setItem('token', token);

// ✅ Better: httpOnly cookie res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'strict' });

// ✅ Best: Short-lived tokens + refresh mechanism ```

3. Token Refresh Pattern

```javascript // Frontend let accessToken = ''; let refreshToken = '';

const refreshAccessToken = async () => { const response = await fetch('/api/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }) });

const { accessToken: newToken } = await response.json(); accessToken = newToken; };

// Axios interceptor for automatic refresh axios.interceptors.response.use( response => response, async error => { if (error.response?.status === 401) { await refreshAccessToken(); // Retry original request return axios(error.config); } return Promise.reject(error); } ); ```

Common Interview Questions

  1. Q: JWT vs Session-based auth? A: JWT is stateless (no server storage), scales better. Sessions store state on server, easier to revoke. JWT is better for microservices/APIs.

  2. Q: Can you decrypt JWT? A: JWT is SIGNED, not encrypted. Anyone can decode and read payload. For sensitive data, use JWE (JSON Web Encryption).

  3. Q: How to invalidate JWT? A: JWTs can't be invalidated (stateless). Solutions: short expiration, blacklist (defeats stateless purpose), or use refresh tokens.

  4. Q: Where to store JWT? A: HttpOnly cookies (best), memory (resets on refresh), localStorage (vulnerable to XSS). Never store in localStorage for sensitive apps.

Security Considerations

DO:

  • Use strong secrets (256+ bits)
  • Set short expiration times
  • Use HTTPS only
  • Validate tokens on every request
  • Implement refresh token rotation

DON'T:

  • Store sensitive data in payload
  • Use weak secrets
  • Skip expiration validation
  • Trust client-side decoding
  • Store in localStorage for sensitive data

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems