Claude Skills Guide

Claude Code for JWT Refresh Token Workflow Guide

JWT (JSON Web Token) authentication has become the standard for modern web applications, but implementing secure token refresh workflows remains a common challenge for developers. This guide walks you through building robust JWT refresh token workflows using Claude Code, with practical patterns you can apply to your projects immediately.

Understanding JWT Refresh Token Fundamentals

Before diving into implementation, it’s essential to understand the moving parts of a secure JWT refresh workflow. When a user authenticates, your server issues two tokens: an access token with a short expiration (typically 15-60 minutes) and a refresh token with a longer lifetime (days or weeks).

The access token grants immediate authorization for API requests, while the refresh token allows clients to obtain new access tokens without requiring the user to re-enter credentials. This separation provides security benefits: if an access token is compromised, the attacker has limited time to use it. The refresh token workflow also enables server-side token revocation without disrupting users significantly.

Claude Code can assist you in implementing this entire workflow, from generating tokens to validating them on each request. The key is structuring your prompts to handle the nuanced logic around token expiration, rotation, and storage securely.

Implementing the Token Generation Workflow

When working with Claude Code to generate JWT tokens, you’ll want to create a clear workflow that handles both access and refresh tokens together. Here’s a practical approach to structuring this implementation:

// Token generation helper with Claude Code assistance
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

function generateTokenPair(userId, userRoles) {
  const accessToken = jwt.sign(
    { userId, roles: userRoles },
    process.env.ACCESS_TOKEN_SECRET,
    { expiresIn: '15m' }
  );

  const refreshToken = crypto.randomBytes(64).toString('hex');
  
  // Store refresh token hash in database (never store plain tokens)
  const refreshTokenHash = crypto
    .createHash('sha256')
    .update(refreshToken)
    .digest('hex');

  return {
    accessToken,
    refreshToken,
    refreshTokenHash
  };
}

Notice the critical security practice: we store only a hash of the refresh token in the database, not the plain token. This ensures that even if your database is compromised, attackers cannot use the stolen refresh tokens. Claude Code can help you identify similar security considerations throughout your implementation.

Building the Token Refresh Endpoint

The refresh endpoint is where most security vulnerabilities appear. Claude Code can guide you through implementing proper validation, rotation, and expiration handling. Here’s a robust implementation pattern:

app.post('/auth/refresh', async (req, res) => {
  const { refreshToken } = req.cookies;
  
  if (!refreshToken) {
    return res.status(401).json({ error: 'Refresh token required' });
  }

  // Hash incoming token and compare with stored hash
  const tokenHash = crypto
    .createHash('sha256')
    .update(refreshToken)
    .digest('hex');

  const storedToken = await RefreshToken.findOne({
    where: { tokenHash, expiresAt: { gt: new Date() } }
  });

  if (!storedToken) {
    return res.status(401).json({ error: 'Invalid or expired refresh token' });
  }

  // Generate new token pair
  const user = await User.findById(storedToken.userId);
  const tokens = generateTokenPair(user.id, user.roles);

  // Rotate: invalidate old refresh token
  await storedToken.destroy();

  // Store new refresh token
  await RefreshToken.create({
    userId: user.id,
    tokenHash: crypto.createHash('sha256').update(tokens.refreshToken).digest('hex'),
    expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
  });

  res.json(tokens);
});

This implementation demonstrates token rotation—each refresh invalidates the old token and issues a new one. This practice limits the window of opportunity for token theft attacks. When prompting Claude Code to help with similar endpoints, explicitly request security best practices like this.

Handling Token Expiration Gracefully

Client-side token handling determines the user experience during token expiration. Your frontend needs to detect 401 responses and automatically attempt refresh before retrying the original request. Here’s a practical approach:

async function authenticatedFetch(url, options, tokenManager) {
  const response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${tokenManager.accessToken}`
    }
  });

  if (response.status === 401) {
    // Attempt token refresh
    const refreshed = await tokenManager.refresh();
    
    if (refreshed) {
      // Retry original request with new token
      return fetch(url, {
        ...options,
        headers: {
          ...options.headers,
          'Authorization': `Bearer ${tokenManager.accessToken}`
        }
      });
    }
    
    // Refresh failed - redirect to login
    window.location.href = '/login';
  }

  return response;
}

Claude Code can help you integrate this pattern into your specific frontend framework, whether you’re using React, Vue, or vanilla JavaScript. The key principle is automatic refresh with clear fallback behavior when refresh fails.

Implementing Secure Logout

Logout functionality often gets overlooked but remains crucial for security. A complete logout should invalidate the refresh token server-side:

app.post('/auth/logout', authenticateToken, async (req, res) => {
  const { refreshToken } = req.cookies;
  
  if (refreshToken) {
    const tokenHash = crypto
      .createHash('sha256')
      .update(refreshToken)
      .digest('hex');
    
    await RefreshToken.destroy({
      where: { tokenHash }
    });
  }

  res.clearCookie('refreshToken');
  res.json({ message: 'Logged out successfully' });
});

You might also want to implement a “logout everywhere” feature that invalidates all refresh tokens for a user—essential for security-sensitive applications when users change passwords or suspect account compromise.

Best Practices for Claude Code JWT Implementation

When working with Claude Code on JWT refresh workflows, keep these actionable recommendations in mind:

First, always use HTTPS in production. JWT tokens transmit sensitive authentication data, and man-in-the-middle attacks can intercept tokens on unencrypted connections.

Second, implement token expiration monitoring. Set up logging for refresh failures to detect potential attacks or implementation bugs early. Claude Code can help you add appropriate logging throughout the flow.

Third, consider refresh token storage carefully. HttpOnly cookies provide the best protection against XSS attacks, while mobile applications might prefer secure storage mechanisms your platform provides.

Fourth, test your token workflow thoroughly. Include tests for normal refresh, concurrent refresh attempts, refresh with invalid tokens, and refresh after logout. Claude Code can generate test cases for these scenarios.

Finally, document your token lifecycle clearly. Understanding when tokens are issued, refreshed, and invalidated helps debugging and security audits significantly.

Conclusion

Implementing secure JWT refresh token workflows requires attention to detail across multiple components: token generation, storage, validation, rotation, and client-side handling. Claude Code serves as an excellent pair programmer for this task, helping you implement these patterns correctly while explaining the security rationale behind each decision.

The key takeaways are straightforward: rotate tokens on refresh, store only hashed tokens, handle expiration gracefully on the client, and test thoroughly. By following these patterns, you’ll build authentication systems that balance security with excellent user experience.

Built by theluckystrike — More at zovo.one