Skip to main content

On This Page

Securing .NET Applications with JWT Refresh Token Rotation

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

JWT Refresh Token Rotation in .NET — Why Your Auth is Probably Broken

Standard JWT implementations often leave accounts vulnerable for up to 7 days if a refresh token is compromised. Token rotation mitigates this risk by revoking and replacing tokens on every use, enabling immediate detection of theft through token lineage tracking.

Why This Matters

In many production environments, developers rely on long-lived static refresh tokens, creating a persistent attack vector. While ideal models assume secure storage, technical reality involves risks like XSS or physical device access; rotating tokens ensures that even if a token is stolen, its utility is limited to a single transaction before triggering an automated security alert that can revoke the entire token family.

Key Insights

  • Default ClockSkew in .NET is 5 minutes, which allows expired tokens to remain valid unless explicitly set to TimeSpan.Zero during configuration.
  • Token lineage tracking via the ReplacedByToken field creates a chain of custody that identifies compromised token reuse and triggers automated revocation.
  • Cryptographically secure token generation using RandomNumberGenerator.GetBytes(64) ensures high entropy for refresh tokens compared to standard GUIDs.
  • Short-lived access tokens (15 minutes) minimize the potential damage window if a bearer token is intercepted in transit.

Working Examples

The RefreshToken entity includes a ReplacedByToken field to create a chain for rotation tracking and compromise detection.

public class RefreshToken
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Token { get; set; }
public DateTime ExpiresAt { get; set; }
public bool IsRevoked { get; set; }
public DateTime? RevokedAt { get; set; }
public string? ReplacedByToken { get; set; }
public Guid UserId { get; set; }
public ApplicationUser User { get; set; }
public bool IsExpired => DateTime.UtcNow >= ExpiresAt;
public bool IsActive => !IsRevoked && !IsExpired;
}

Implementation of secure refresh token generation using 64-bit random byte arrays.

public async Task<RefreshToken> GenerateRefreshTokenAsync(
Guid userId, CancellationToken ct = default)
{
var refreshToken = new RefreshToken
{
Token = Convert.ToBase64String(
RandomNumberGenerator.GetBytes(64)),
ExpiresAt = DateTime.UtcNow.AddDays(7),
UserId = userId
};
_context.RefreshTokens.Add(refreshToken);
await _context.SaveChangesAsync(ct);
return refreshToken;
}

Practical Applications

  • Use Case: Storing refresh tokens in httpOnly cookies to prevent JavaScript-based XSS attacks that target localStorage.
  • Pitfall: Failing to invalidate all refresh tokens upon a password change, which leaves existing sessions active for potential attackers.
  • Use Case: Enforcing environment-specific JWT secrets to ensure that a compromise in a development environment does not affect production security.

References:

Continue reading

Next article

LangChain Deep Agents: A Structured Runtime for Multi-Step AI Agent Orchestration

Related Content