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

```csharp
public interface IJwtReplayCache
```

Derived  
↳ [DistributedJwtReplayCache](https://www.abblix.com/en/docs/api/abblix-oidc-server/Abblix.Oidc.Server.Features.ReplayPrevention.DistributedJwtReplayCache 'Abblix\.Oidc\.Server\.Features\.ReplayPrevention\.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 {#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/en/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.
