Skip to main content

IJwtReplayCache Interface

Abblix.Oidc.Server

Abblix.Oidc.Server.Features.ReplayPrevention

IJwtReplayCache Interface

Tracks JWT IDs (jti claims) presented to the server, so a JWT-bearing flow can detect replay attempts. Both RFC 7523 §5.2 (JWT-bearer-grant assertion replay) and RFC 9449 §11.1 (DPoP proof replay) want this primitive; it is intentionally namespace-neutral so a single distributed-cache instance serves every consumer.

public interface IJwtReplayCache

Derived
DistributedJwtReplayCache

Remarks

Implementations should use distributed storage (e.g., Redis) so multi-instance deployments share the replay-protection state.

Methods

IJwtReplayCache.TryAddAsync(string, Nullable<DateTimeOffset>) Method

Records a fresh jti, returning true only on the first call for that value. The single-call shape is atomic-by-contract: implementations are expected to use the backend's compare-and-set primitive so concurrent presenters of the same jti cannot both observe a miss and both succeed.

System.Threading.Tasks.Task<bool> TryAddAsync(string jti, System.Nullable<System.DateTimeOffset> expiresAt);

Parameters

jti System.String

The JWT ID (jti claim) to record.

expiresAt System.Nullable<System.DateTimeOffset>

Latest moment a same-jti replay could still pass the iat-window check; the cache entry only needs to persist that long. null defers to the implementation's default TTL.

Returns

System.Threading.Tasks.Task<System.Boolean>
true if the jti was newly recorded (proof is fresh); false if it was already present (replay detected).

Remarks

Atomic-capable backends close the race natively: Redis SET … NX EX (via StackExchange.Redis), SQL INSERT … ON CONFLICT DO NOTHING, Memcached add, in-memory ConcurrentDictionary.TryAdd.

The default implementation DistributedJwtReplayCache uses Microsoft.Extensions.Caching.Distributed.IDistributedCache, which exposes only Get + Set and no compare-and-set primitive. It therefore provides only a probabilistic guarantee: two concurrent presenters of the same jti can both observe a miss before either writes. The race window is bounded by the cache round-trip and RFC 9449 §11.1 accepts probabilistic replay defence — but hosts that need strict atomicity should override the registration with a backend-aware implementation that bypasses Microsoft.Extensions.Caching.Distributed.IDistributedCache and talks to the chosen backend's atomic primitive directly.