Repository navigation
fix: bind agent and MCP sessions to their owner - #506
Merged
Merged
Conversation
New Cloud session ids start with the owner prefix of the signed-in user (first 16 hex characters of SHA-256 over the token's sub), defined once in shared/session_owner.py and mirrored in web-ui/src/lib/sessionId.ts. - Agent entry (agent/session_guard.py, called from basic_agent.agent_stream before the cancel registry): an owner-prefixed id is admitted only for its owner; an id of an earlier format is admitted when AgentCore Memory holds this user's events for it (one ListEvents call, maxResults=1, positive results cached per process); each process serves the first user it admits. Refusals are error events with codes auth / bad_input (header and payload ids differ) / session_reload_required / service_unavailable, no details. - Agent -> MCP: Mcp-Session-Id always carries the owner prefix (earlier ids get it prepended; composer groups <id>-gN keep it). - MCP server (_CaptureHeadersMiddleware): an owner-prefixed Mcp-Session-Id or runtime session header of another user gets 403, and each process serves one user. Requests without authorization (health checks) pass unchanged. - Web UI: new chats and decks without a linked chat get owner-prefixed ids; a linked chat of an earlier format is kept while it has history. New error code session_reload_required with en/ja message. Local mode unchanged. - agent/Dockerfile copies shared/session_owner.py; CHANGELOG and docs/en/security.md describe the binding. Checks (run on this tree plus the package.json change of the next commit): - make lint: pass (ruff, all checks passed) - make test: pass (2146 passed, 18 skipped) - uv run pytest tests/test_remote_session_gate.py tests/test_remote_warmup_preload.py -q: pass (23 passed, none skipped) - i18n en/ja key parity: OK - cd web-ui && npx eslint src/ --quiet: pass - npx tsc --noEmit: pass - npm test: pass (75 files, 639 tests) - npm run build:cloud: pass - ash scan --mode local --fail-on-findings (uvx ASH v3): pass, exit 0
Already in the lockfile at 5.2.0 as an aws-amplify dependency; lib/sessionId.ts now imports it. Checks: npx -y npm@10.8.2 install --package-lock-only: pass; npx -y npm@10.8.2 ci --dry-run: pass (exit 0). Code checks are in the previous commit.
Keeps both behaviours: the agent image copies turn_fence.py and the session_owner module; the MCP server imports both the session owner helpers and the write fence. The session check still runs before the cancel registry and the turn fence. The fence entrypoint tests now use a session id owned by their test user, like the other entrypoint tests. Checks run on this commit: - make lint: passed (ruff, no findings) - make test: 2177 passed, 18 skipped - uv run pytest tests/test_remote_session_gate.py tests/test_remote_write_fence.py -q: 31 passed - ash scan --mode local --fail-on-findings: exit 0 (bandit, checkov, detect-secrets, npm-audit, semgrep PASSED; cdk-nag, cfn-nag, grype, opengrep, syft MISSING locally) - web-ui: no changes from main, checks not re-run
The pinned owner_prefix() vectors are 16-hex-digit hashes of fixed test user ids. detect-secrets reports them as high-entropy hex strings, which fails the ASH scan; annotate them with pragma: allowlist secret, as infra/ does for CDK logical-id hashes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Agent and remote MCP sessions in Cloud mode are now bound to the user who owns them.
sub. The derivation is defined once inshared/session_owner.pyand mirrored inweb-ui/src/lib/sessionId.ts.agent/session_guard.py, called frombasic_agent.agent_streambefore the cancel registry): an owner-prefixed id is admitted only for its owner. Each runtime process serves the first user it admits. Refusals are returned as error events with the codesauth,bad_input(header and payload ids differ),session_reload_required, orservice_unavailable, without further detail.ListEventscall withmaxResults=1, and positive results are cached per process.Mcp-Session-Idalways carries the owner prefix. Earlier ids get it prepended, and composer groups (<id>-gN) keep it._CaptureHeadersMiddleware): an owner-prefixedMcp-Session-Idor runtime session header that belongs to a different user gets a 403, and each process serves one user. Requests without authorization (health checks) pass through unchanged. The session check runs before the cancel registry and the write fence.session_reload_requiredhas en/ja messages.@aws-crypto/sha256-jsis now a direct dependency; it was already in the lockfile at 5.2.0 through aws-amplify.agent/Dockerfilecopiesshared/session_owner.py.CHANGELOG.mdanddocs/en/security.mddescribe the binding.Compatibility
Existing decks keep their chat. A chat id from before this change is kept while it has history and is admitted when AgentCore Memory holds that user's history for it; a deck without history continues under the user's own owner-prefixed id. A chat tab opened before the update may be asked to reload the page once (
session_reload_required).Deploy order: Web UI first, then SdpmRuntime, then SdpmAgent.
Local mode
Unchanged. Local mode has no auth and keeps its current session ids.
Checks run
On the main change (
face6403, including thepackage.jsonchange fromd28354f7):make lint: pass (ruff)make test: pass (2146 passed, 18 skipped)uv run pytest tests/test_remote_session_gate.py tests/test_remote_warmup_preload.py -q: pass (23 passed)cd web-ui && npx eslint src/ --quiet,npx tsc --noEmit,npm test(75 files, 639 tests),npm run build:cloud: passash scan --mode local --fail-on-findings: exit 0On the lockfile (
d28354f7):npx -y npm@10.8.2 install --package-lock-onlyandnpx -y npm@10.8.2 ci --dry-run: passAfter merging current
main(#505 write fence,df51fab3):make lint: passmake test: 2177 passed, 18 skippeduv run pytest tests/test_remote_session_gate.py tests/test_remote_write_fence.py -q: 31 passedash scan --mode local --fail-on-findings: exit 0 (bandit, checkov, detect-secrets, npm-audit, semgrep passed; cdk-nag, cfn-nag, grype, opengrep, syft not available locally)