#### [Abblix\.Oidc\.Server](https://www.abblix.com/docs/api/abblix-oidc-server 'index')
### [Abblix\.Oidc\.Server\.Features\.ReplayPrevention](https://www.abblix.com/docs/api/abblix-oidc-server/Abblix.Oidc.Server.Features.ReplayPrevention '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 §3 \(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\.

```csharp
public interface IJwtReplayCache
```

Derived  
↳ [DistributedJwtReplayCache](https://www.abblix.com/docs/api/abblix-oidc-server/Abblix.Oidc.Server.Features.ReplayPrevention.DistributedJwtReplayCache 'Abblix\.Oidc\.Server\.Features\.ReplayPrevention\.DistributedJwtReplayCache')

### Remarks
The primitive now lives in Abblix\.JWT as [IReplayCache](https://www.abblix.com/docs/api/abblix-jwt/Abblix.Jwt.ReplayPrevention.IReplayCache 'Abblix\.Jwt\.ReplayPrevention\.IReplayCache'), one layer below this
package, because Security Event Token receivers need the same reserve\-and\-check and cannot
reference the OpenID Connect server to get it\. This contract remains registered and working
for host code that names it, and its default implementation stores through the moved one, so
both spellings share a single set of entries\.
### Methods

## IJwtReplayCache\.TryAddAsync\(string, Nullable\<DateTimeOffset\>\) Method {#Abblix.Oidc.Server.Features.ReplayPrevention.IJwtReplayCache.TryAddAsync(string,System.Nullable_System.DateTimeOffset_)}

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\.

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

###### `jti` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String') {#Abblix.Oidc.Server.Features.ReplayPrevention.IJwtReplayCache.TryAddAsync(string,System.Nullable_System.DateTimeOffset_).jti}

The JWT ID \(jti claim\) to record\.

###### `expiresAt` [System\.Nullable&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.nullable-1 'System\.Nullable\`1')[System\.DateTimeOffset](https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset 'System\.DateTimeOffset')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.nullable-1 'System\.Nullable\`1') {#Abblix.Oidc.Server.Features.ReplayPrevention.IJwtReplayCache.TryAddAsync(string,System.Nullable_System.DateTimeOffset_).expiresAt}

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&lt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')[System\.Boolean](https://learn.microsoft.com/en-us/dotnet/api/system.boolean 'System\.Boolean')[&gt;](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1 'System\.Threading\.Tasks\.Task\`1')  
`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](https://www.abblix.com/docs/api/abblix-oidc-server/Abblix.Oidc.Server.Features.ReplayPrevention.DistributedJwtReplayCache 'Abblix\.Oidc\.Server\.Features\.ReplayPrevention\.DistributedJwtReplayCache') uses
[Microsoft\.Extensions\.Caching\.Distributed\.IDistributedCache](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.distributed.idistributedcache '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](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.distributed.idistributedcache 'Microsoft\.Extensions\.Caching\.Distributed\.IDistributedCache') and
talks to the chosen backend's atomic primitive directly.
