DistributedJwtReplayCache Class
Abblix.Oidc.Server
Abblix.Oidc.Server.Features.ReplayPrevention
DistributedJwtReplayCache Class
Distributed cache implementation of IJwtReplayCache for JWT replay protection. Uses Microsoft.Extensions.Caching.Distributed.IDistributedCache to store JTIs, enabling multi-instance deployments.
public class DistributedJwtReplayCache : Abblix.Oidc.Server.Features.ReplayPrevention.IJwtReplayCache
Inheritance System.Object → DistributedJwtReplayCache
Implements IJwtReplayCache
Remarks
This implementation stores JTIs with automatic expiration matching the JWT's lifetime. Works with Redis, SQL Server, NCache, or any IDistributedCache implementation. Clock skew buffer is configurable via ClockSkew.
Constructors
DistributedJwtReplayCache(ILogger<DistributedJwtReplayCache>, IDistributedCache, IOptionsMonitor<OidcOptions>, TimeProvider) Constructor
Distributed cache implementation of IJwtReplayCache for JWT replay protection. Uses Microsoft.Extensions.Caching.Distributed.IDistributedCache to store JTIs, enabling multi-instance deployments.
public DistributedJwtReplayCache(Microsoft.Extensions.Logging.ILogger<Abblix.Oidc.Server.Features.ReplayPrevention.DistributedJwtReplayCache> logger, Microsoft.Extensions.Caching.Distributed.IDistributedCache cache, Microsoft.Extensions.Options.IOptionsMonitor<Abblix.Oidc.Server.Common.Configuration.OidcOptions> options, System.TimeProvider timeProvider);
Parameters
logger Microsoft.Extensions.Logging.ILogger<DistributedJwtReplayCache>
Logger for recording replay detection events.
cache Microsoft.Extensions.Caching.Distributed.IDistributedCache
The distributed cache for storing JTIs.
options Microsoft.Extensions.Options.IOptionsMonitor<OidcOptions>
JWT Bearer options for configurable settings like clock skew.
timeProvider System.TimeProvider
Provides access to the current time.
Remarks
This implementation stores JTIs with automatic expiration matching the JWT's lifetime. Works with Redis, SQL Server, NCache, or any IDistributedCache implementation. Clock skew buffer is configurable via ClockSkew.
Methods
DistributedJwtReplayCache.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.
public 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.
Implements TryAddAsync(string, Nullable<DateTimeOffset>)
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.