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

Protecting Against Token Replay Attacks

Implement nonces and jti claims to ensure tokens are used only once.

In this lesson, you will learn how to prevent attackers from intercepting and reusing intercepted JSON Web Tokens (JWTs) by implementing jti (JWT ID) claims and nonce validation.

The Replay Attack Problem

A token replay attack occurs when an attacker captures a valid, intercepted JWT and sends it to your API again. Because the token is mathematically valid and hasn't expired, the server might treat it as a legitimate request from the original user. To stop this, we need to ensure each token is "one-time use" or at least uniquely identifiable.

Implementing the JTI Claim

The jti (JWT ID) claim acts as a unique fingerprint for a specific token. When you generate a token, you include a unique identifier:

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    new Claim(ClaimTypes.Name, "user123")
};

When your API receives a request, it checks this jti. To prevent replay, the server must keep a "blacklist" or "used list" of these IDs.

Server-Side Validation Logic

You can use a fast, distributed cache like Redis to store the jti for the duration of the token's lifetime. Here is the conceptual flow for validating incoming requests:

public async Task<bool> IsTokenValid(string jti, TimeSpan expiration)
{
    // Check if JTI exists in the cache
    if (await _cache.GetAsync(jti) != null)
    {
        return false; // Token has already been used!
    }

    // Mark JTI as used for the duration of the token's life
    await _cache.SetStringAsync(jti, "used", new DistributedCacheEntryOptions 
    { 
        AbsoluteExpirationRelativeToNow = expiration 
    });

    return true;
}

Using Nonces for One-Time Requests

While jti is great for identifying tokens, a nonce (number used once) is typically used during the initial authentication handshake (common in OIDC flows). By requiring a client to send a unique, cryptographically strong random value that your server tracks, you ensure that even if an attacker steals the request, they cannot replay it because the server will reject any nonce it has already processed.

Key Takeaway: By embedding a unique jti claim in your tokens and tracking them in a high-speed cache, you can detect and reject replayed tokens, effectively neutralizing the threat of intercepted credentials.