.NET: [Experimental] Extend A2A task store with isolation key scoping - #7504
Conversation
There was a problem hiding this comment.
Pull request overview
Adds tenant-scoped isolation to A2A task storage, aligning task operations with the existing session-store isolation model to prevent cross-tenant task access in multi-user hosts.
Changes:
- Introduces
IsolationKeyScopedTaskStoreto scope task IDs and persistedAgentTask.ContextIdby isolation key (and un-scope on reads). - Updates A2A server DI wiring to automatically wrap the configured
ITaskStorewith tenant isolation when building anA2AServer. - Adds unit tests plus updates trust-model/sample comments to reflect that both
contextIdandtaskIdare untrusted wire identifiers.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.Hosting.A2A/IsolationKeyScopedTaskStore.cs | New delegating task store that scopes/unscopes IDs and filters list results by tenant isolation key. |
| dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.cs | Wraps the resolved ITaskStore in IsolationKeyScopedTaskStore and updates trust-model remarks. |
| dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/IsolationKeyScopedTaskStoreTests.cs | New unit tests for task store scoping/unscoping and tenant separation behaviors. |
| dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Program.cs | Updates sample commentary to clarify sessions and tasks require isolation in production. |
| dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs | Updates sample commentary to clarify sessions and tasks require isolation in production. |
15098e7 to
736fa72
Compare
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 56%
✓ Security Reliability
Note: every file-read/grep/bash call in this session was denied by the environment, so I could not complete the bounded repo verification pass. Findings below are limited to what is provable from the diff text alone; anything requiring inspection of A2A library types (AgentTask/ListTasksRequest/ListTasksResponse nullability and paging semantics), IsolationKeyScopedAgentSessionStore, or the surrounding A2AServer wiring was omitted rather than speculated on. The isolation scheme itself looks sound from the diff: EscapeIsolationKey escapes both backslash and colon before joining with the '::' delimiter (lines 155-161), so an escaped key can never contain a bare '::' and one tenant's prefix cannot be a prefix of another's — no cross-tenant key-collision path is visible. Scoping is applied consistently to Get/Save/Delete, and clone-rather-than-mutate is used for both AgentTask and ListTasksRequest, which avoids leaking scoped ids back onto the wire. The one reliability concern I can substantiate from the diff is that ListTasksAsync applies isolation filtering AFTER the inner store has already paginated, which can yield short or empty pages for a tenant in a shared store. This does not leak data, so I am not requesting changes.
✓ Test Coverage
The wrapper itself has strong direct unit coverage, but the security-critical automatic DI wiring is untested.
✓ Failure Modes
File-system access was blocked in this session (all view/grep/glob/bash calls were denied), so I could only review the diff text itself and deliberately withheld any finding that would require reading surrounding source (e.g., the session-store wrapper's strict-mode semantics, ITaskStore/AgentTask nullability contracts, or A2AServer's task-store call sites). Within the diff alone, the isolation logic in IsolationKeyScopedTaskStore is internally consistent and matches the assertions in IsolationKeyScopedTaskStoreTests.cs. One operational failure mode is visible purely from the diff: A2AServerServiceCollectionExtensions.cs now unconditionally wraps a user-registered ITaskStore, changing the persisted key layout from
taskIdtokey::taskIdand the persisted ContextId tokey::ctx. For any existing deployment that already has both a SessionIsolationKeyProvider and a durable task store, previously persisted tasks silently become unreachable (GetTaskAsync returns null, ListTasksAsync filters them out via IsInScope because their ContextId lacks the prefix) rather than surfacing an error — which contradicts the PR's "not a breaking change" checkbox for that configuration. This is a migration/documentation concern rather than a code defect, so I am not requesting changes.
✓ Design Approach
I was unable to complete a verification pass: every file-access tool (bash, view, grep, glob) returned a permission error in this session, so I could not open ITaskStore/AgentTask definitions, the existing IsolationKeyScopedAgentSessionStore, or the surrounding CreateA2AServer code needed to substantiate any design-level concern. Reading the diff alone, the approach mirrors the existing session-store isolation pattern, the ListTasksAsync scope/unscope logic is internally consistent with the tests in the diff (scoped ContextId on write, prefix filter plus unscope on read, non-mutating clone of both AgentTask and ListTasksRequest), and the TotalSize limitation is explicitly documented in the XML remarks. I have no finding that meets the required file:line evidence bar, so I am reporting none rather than speculating.
Suggestions
- Confirm that
strict: isolationKeyProvider != nulldoes not cause task-store operations to throw on non-HTTP or unauthenticated paths where the provider may legitimately return null.
Automated review by SergeyMenshykh's agents
Wrap ITaskStore with IsolationKeyScopedTaskStore when a SessionIsolationKeyProvider is registered, mirroring the existing session store isolation pattern. This ensures task operations are scoped per tenant in multi-user deployments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: adc30d6c-ce66-40bb-933e-9801c2156cda
736fa72 to
6c00d61
Compare
Motivation & Context
The A2A hosting layer applies isolation key scoping to the session store but not the task store. This PR extends the same isolation pattern to cover task operations.
Description & Review Guide
IsolationKeyScopedTaskStoreclass that wrapsITaskStorewith per-caller key scoping (mirrorsIsolationKeyScopedAgentSessionStore).CreateA2AServerautomatically wraps the task store when aSessionIsolationKeyProvideris registered.Contribution Checklist