Saltar al contenido
Esta página aún no está traducida.

DistributedJwtReplayCache Class

The deprecated contract's default implementation, kept so a host that resolves IJwtReplayCache still receives a working object. It stores nothing of its own: every reservation goes to the same IReplayCache the server's own consumers use, so the deprecated and current spellings share one set of entries and cannot disagree about whether an identifier has been seen.

C#
public class DistributedJwtReplayCache : Abblix.Oidc.Server.Features.ReplayPrevention.IJwtReplayCache

Inheritance System.Object → DistributedJwtReplayCache

Implements IJwtReplayCache

Remarks

The only behaviour left here is the one the current contract deliberately dropped: an absent expiry. The moved contract requires its caller to say when an identifier stops being worth remembering, because a cache that guesses either outlives or forgets the window its caller actually validates against. This shim keeps guessing on its callers' behalf, with the hour the deprecated contract names.

Constructors

DistributedJwtReplayCache(IReplayCache, TimeProvider) Constructor

The deprecated contract's default implementation, kept so a host that resolves IJwtReplayCache still receives a working object. It stores nothing of its own: every reservation goes to the same IReplayCache the server's own consumers use, so the deprecated and current spellings share one set of entries and cannot disagree about whether an identifier has been seen.

C#
public DistributedJwtReplayCache(Abblix.Jwt.ReplayPrevention.IReplayCache replayCache, System.TimeProvider timeProvider);

Parameters

replayCache IReplayCache

Where the reservation lands.

timeProvider System.TimeProvider

Turns an absent expiry into an absolute moment.

Remarks

The only behaviour left here is the one the current contract deliberately dropped: an absent expiry. The moved contract requires its caller to say when an identifier stops being worth remembering, because a cache that guesses either outlives or forgets the window its caller actually validates against. This shim keeps guessing on its callers' behalf, with the hour the deprecated contract names.

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.

C#
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.