Description
As of v4.0.0b3, IdentityAssertionValidator keeps its jti replay cache in process memory:
# fastmcp_slim/fastmcp/server/auth/identity_assertion.py
self._jti_cache: dict[str, float] = {}
self._jti_cache_max_size = 10000
The docstring is candid that this "is per-process, so replay protection is not shared across horizontally-scaled workers." In practice that means SEP-990's replay protection is defeated by exactly the deployments identity assertion is aimed at: any server running more than one replica (or any autoscaling platform — Cloud Run, ECS, Kubernetes HPA) will accept the same assertion once per instance. We verified this cross-instance replay in a two-process trial. The known-gaps page currently classifies multi-replica concerns as "deployment configuration rather than protocol gaps", but this one is a security property of the validator itself, not a deployment knob — there is no configuration that fixes it today.
Proposed solution
Accept a pluggable KV store on IdentityAssertionValidator, following the precedent FastMCP already set with KeyValueResponseCacheStore (memory / Redis / filetree backends behind one interface). Something like:
IdentityAssertionValidator(..., jti_store=SomeKeyValueStore(...))
with the current in-process dict as the default. The important part is that it's the store interface that's pluggable, not a hard dependency on any particular backend — deployments that already run Postgres (or any KV-capable store) can back replay state with what they have, without adding Redis to their stack. A TTL-honoring setnx-style primitive (set-if-absent with expiry) is all the validator needs for an atomic jti burn.
Raising this before 4.0 GA in case it has API-shape implications. Happy to provide the cross-instance repro details if useful.
Description
As of
v4.0.0b3,IdentityAssertionValidatorkeeps its jti replay cache in process memory:The docstring is candid that this "is per-process, so replay protection is not shared across horizontally-scaled workers." In practice that means SEP-990's replay protection is defeated by exactly the deployments identity assertion is aimed at: any server running more than one replica (or any autoscaling platform — Cloud Run, ECS, Kubernetes HPA) will accept the same assertion once per instance. We verified this cross-instance replay in a two-process trial. The known-gaps page currently classifies multi-replica concerns as "deployment configuration rather than protocol gaps", but this one is a security property of the validator itself, not a deployment knob — there is no configuration that fixes it today.
Proposed solution
Accept a pluggable KV store on
IdentityAssertionValidator, following the precedent FastMCP already set withKeyValueResponseCacheStore(memory / Redis / filetree backends behind one interface). Something like:with the current in-process dict as the default. The important part is that it's the store interface that's pluggable, not a hard dependency on any particular backend — deployments that already run Postgres (or any KV-capable store) can back replay state with what they have, without adding Redis to their stack. A TTL-honoring
setnx-style primitive (set-if-absent with expiry) is all the validator needs for an atomic jti burn.Raising this before 4.0 GA in case it has API-shape implications. Happy to provide the cross-instance repro details if useful.