Course contentsShow
← C#
Lesson 1234 of 1,90811. Identity, Security, and ClaimsFree lesson

JWT Lifecycle Management

Handle token issuance, refresh, and rotation for stateless authentication.

In this lesson, you will learn how to maintain secure, stateless authentication by managing the lifecycle of JWTs through issuance, refresh tokens, and rotation.

The Problem with Stateless Tokens

Because JWTs are stateless, once issued, they remain valid until they expire. If a malicious actor steals a token, they have access until that expiration time, regardless of what happens on the server. To balance security with user experience, we split the authentication into two parts: a short-lived Access Token and a long-lived Refresh Token.

The Lifecycle Flow

  1. Issuance: Upon login, the server issues an AccessToken (valid for minutes) and a RefreshToken (valid for days/weeks). The refresh token is stored in your database, linked to the user.
  2. Access: The client uses the AccessToken for API calls. Once it expires, the client sends the RefreshToken to a dedicated /refresh endpoint.
  3. Rotation: To prevent reuse of leaked tokens, we implement Refresh Token Rotation. Every time a refresh token is used, the server invalidates the old one and issues a new pair (a new access token and a brand-new refresh token).

Implementation Logic

When the /refresh endpoint is hit, you must validate the refresh token against your database. If valid, you issue the new pair and mark the previous refresh token as "used" or delete it.

public async Task<TokenResponse> RefreshTokenAsync(string oldRefreshToken)
{
    var storedToken = await _db.RefreshTokens.FirstOrDefaultAsync(t => t.Token == oldRefreshToken);
    
    // Security check: If the token was already used or revoked, 
    // it might be a replay attack. Revoke all tokens for this user.
    if (storedToken == null || storedToken.IsUsed || storedToken.IsExpired)
        throw new SecurityTokenException("Invalid refresh token");

    storedToken.IsUsed = true;
    
    // Issue new pair
    var newAccessToken = GenerateAccessToken(storedToken.User);
    var newRefreshToken = GenerateRefreshToken();
    
    await _db.RefreshTokens.AddAsync(newRefreshToken);
    await _db.SaveChangesAsync();
    
    return new TokenResponse(newAccessToken, newRefreshToken);
}

By rotating tokens, if a refresh token is stolen, the attacker can only use it once. If the legitimate user then attempts to use their (now invalidated) token, the server detects the discrepancy and can proactively revoke all active sessions for that user.

Key Takeaway: Use short-lived access tokens combined with refresh token rotation to minimize the window of opportunity for attackers while maintaining a seamless user experience.