Skip to content
intermediatePhase 41 · Frontend Security

Token Storage

Store JWT tokens securely: httpOnly cookies vs memory storage.

30m
0 problems
Topic Progress0%

Storage Options

Storage Options

localStorage

// Easy but vulnerable to XSS
localStorage.setItem('token', jwtToken);
const token = localStorage.getItem('token');

// Can be accessed by any JavaScript
// document.cookie won't show it
// But XSS can steal it

sessionStorage

// Similar to localStorage but per-tab
sessionStorage.setItem('token', jwtToken);

// Cleared when tab closes
// Still vulnerable to XSS

Cookies (HttpOnly)

// Most secure option
res.cookie('session', token, {
  httpOnly: true,  // No JavaScript access
  secure: true,    // HTTPS only
  sameSite: 'strict',
});

// Automatically sent with requests
// Not accessible via JavaScript

Comparison

Storage XSS Risk CSRF Risk Access
localStorage High Low JavaScript
sessionStorage High Low JavaScript
Cookie (HttpOnly) Low High* HTTP only
In-memory Low Low JavaScript

*Requires CSRF protection

In-Memory Storage

// Most secure, but lost on refresh
let token = null;

function setToken(newToken) {
  token = newToken;
}

function getToken() {
  return token;
}

// Or use React state
const [token, setToken] = useState(null);

JWT Security

JWT Security

JWT Structure

Header.Payload.Signature

Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "sub": 123, "name": "John", "exp": 1234567890 }
Signature: HMAC-SHA256(base64(header) + "." + base64(payload), secret)

JWT Storage

// Option 1: HttpOnly cookie (recommended)
res.cookie('jwt', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 15 * 60 * 1000, // 15 minutes
});

// Option 2: In-memory (for SPAs)
let accessToken = null;

async function login() {
  const response = await fetch('/api/login', {
    method: 'POST',
    body: JSON.stringify({ email, password }),
  });
  const { token } = await response.json();
  accessToken = token; // Store in memory
}

// Option 3: Short-lived in localStorage (less secure)
localStorage.setItem('token', token);
// But requires refresh token mechanism

Token Refresh

// Refresh token in HttpOnly cookie
res.cookie('refreshToken', refreshToken, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});

// Access token in memory
let accessToken = null;

async function refreshAccessToken() {
  const response = await fetch('/api/refresh', {
    method: 'POST',
    credentials: 'include', // Send cookies
  });
  const { token } = await response.json();
  accessToken = token;
}

// Auto-refresh before expiration
setInterval(refreshAccessToken, 14 * 60 * 1000); // Every 14 minutes

JWT Best Practices

  1. Never store secrets in JWT
  2. Use short expiration for access tokens (15 min)
  3. Use refresh tokens for long-lived sessions
  4. Store tokens securely (HttpOnly cookie or memory)
  5. Validate tokens server-side
  6. Use strong secrets
  7. Implement token revocation
  8. Use HTTPS only

Token Security

Token Security

Preventing Token Theft

// 1. Use HttpOnly cookies
res.cookie('token', jwt, { httpOnly: true });

// 2. Set short expiration
const token = jwt.sign(payload, secret, { expiresIn: '15m' });

// 3. Use refresh tokens
const refreshToken = jwt.sign(payload, secret, { expiresIn: '7d' });

// 4. Implement token revocation
const revokedTokens = new Set();

function isTokenRevoked(token) {
  return revokedTokens.has(token);
}

// 5. Use token binding
const token = jwt.sign(
  { ...payload, sessionId: req.sessionId },
  secret
);

Token in Transit

// Always use HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
    return res.redirect(`https://${req.header('host')}${req.url}`);
  }
  next();
});

// Use Authorization header for APIs
fetch('/api/data', {
  headers: {
    'Authorization': `Bearer ${token}`,
  },
});

// Or use cookies (automatically sent)
fetch('/api/data', {
  credentials: 'include',
});

Token Validation

// Server-side validation
function verifyToken(token) {
  try {
    const decoded = jwt.verify(token, secret);
    
    // Check if token is revoked
    if (isTokenRevoked(token)) {
      throw new Error('Token revoked');
    }
    
    // Check expiration
    if (decoded.exp < Date.now() / 1000) {
      throw new Error('Token expired');
    }
    
    return decoded;
  } catch (error) {
    throw new Error('Invalid token');
  }
}

// Middleware
function authMiddleware(req, res, next) {
  const token = req.cookies.token || req.header('Authorization')?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'No token' });
  }

  try {
    req.user = verifyToken(token);
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Common Mistakes

// Bad: Storing token in localStorage (XSS vulnerable)
localStorage.setItem('token', jwt);

// Bad: Storing secrets in JWT
const token = jwt.sign({ secret: 'mysecret' }, key);

// Bad: Long expiration without refresh
const token = jwt.sign(payload, secret, { expiresIn: '30d' });

// Good: HttpOnly cookie with short expiration
res.cookie('token', jwt, { httpOnly: true, maxAge: 900000 });

// Good: Refresh token mechanism
const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' });
const refreshToken = jwt.sign(payload, secret, { expiresIn: '7d' });

Testing Token Security

describe('Token Security', () => {
  it('should not expose token in response body', async () => {
    const response = await request(app)
      .post('/login')
      .send({ email: 'test@example.com', password: 'password' });

    expect(response.body.token).toBeUndefined();
  });

  it('should set HttpOnly cookie', async () => {
    const response = await request(app)
      .post('/login')
      .send({ email: 'test@example.com', password: 'password' });

    const cookies = response.headers['set-cookie'];
    expect(cookies).toBeDefined();
    expect(cookies[0]).toContain('HttpOnly');
  });
});

Practice Problems

0/3solved
Build Token Storage Component

Create a reusable React component implementing Token Storage. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Token Storage Testing

Write unit and integration tests for Token Storage using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Token Storage Performance

Optimize Token Storage for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is the most secure way to store JWT?

Question 1 options

2. Why use short-lived access tokens?

Question 2 options

3. What is the primary purpose of Token Storage?

Question 3 options

4. What is a common mistake when implementing Token Storage?

Question 4 options

Flashcards

Question

What is JWT?

Answer

JSON Web Token - a compact, URL-safe token format for secure information exchange.

Question

What is the safest token storage?

Answer

HttpOnly cookies - not accessible via JavaScript, preventing XSS theft.

Question

Why use refresh tokens?

Answer

To obtain new access tokens without requiring re-authentication.

Question

What is token revocation?

Answer

Invalidating a token before its expiration, used for logout or security.

Question

What is Token Storage?

Answer

Token Storage is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.HttpOnly cookies are the most secure token storage
  • 2.Use short-lived access tokens with refresh tokens
  • 3.Never store tokens in localStorage
  • 4.Always use HTTPS for token transmission
  • 5.Implement token revocation for security

Interview Tips

  • Explain token storage security trade-offs
  • Discuss JWT structure and security
  • Know refresh token patterns

Cheat Sheet

Token Storage Cheat Sheet

Storage Options

  • localStorage: XSS vulnerable
  • sessionStorage: XSS vulnerable
  • HttpOnly cookie: Most secure
  • In-memory: Secure but lost on refresh

JWT Structure

Header.Payload.Signature

Best Practices

  • Use HttpOnly cookies
  • Short access token (15 min)
  • Long refresh token (7 days)
  • Always use HTTPS
  • Validate server-side
  • Implement revocation

Refresh Flow

  1. Access token in memory/HttpOnly
  2. Refresh token in HttpOnly cookie
  3. Auto-refresh before expiry
  4. Re-authenticate on refresh failure