fix(gateway): allow bearer-auth session history reads#81815
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts the session history HTTP endpoint auth path so shared-secret Bearer callers can read session history consistently with OpenAI-compatible gateway surfaces.
Changes:
- Switches
/sessions/:key/historyand its SSE reauth check to use OpenAI-compatible operator scope resolution. - Updates session history auth tests to keep WS scope rejection coverage and add Bearer default-scope HTTP read coverage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/gateway/sessions-history-http.ts |
Uses OpenAI-compatible scope resolution for history reads and SSE authorization checks. |
src/gateway/sessions-history-http.test.ts |
Updates auth regression expectations and adds a Bearer-auth history read test. |
639e0ec to
6a6b588
Compare
|
Codex review: needs maintainer review before merge. Workflow note: Future ClawSweeper reviews update this same comment in place. How this review workflow works
Summary Reproducibility: yes. from source inspection, though I did not execute tests in this read-only review. Current main routes HTTP session history through the strict resolver, which returns no scopes for shared-secret bearer auth and therefore fails PR rating Rank-up moves:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. Real behavior proof Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land the narrow resolver, docs, and regression-test change if maintainers accept HTTP session history as a shared-secret full-operator surface, while keeping internal namespace blocking tracked separately. Do we have a high-confidence way to reproduce the issue? Yes from source inspection, though I did not execute tests in this read-only review. Current main routes HTTP session history through the strict resolver, which returns no scopes for shared-secret bearer auth and therefore fails Is this the best way to solve the issue? Yes, with the explicit boundary caveat. Switching both initial REST authorization and SSE reauthorization to the shared-secret resolver is the narrowest maintainable code fix if maintainers accept session history as part of the full-operator HTTP secret model. Label changes:
Label justifications:
Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 4399eee6e0e4. |
6a6b588 to
0d5de19
Compare
6a6f70a to
e3eb989
Compare
This comment was marked as low quality.
This comment was marked as low quality.
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
e3eb989 to
0beaddc
Compare
|
ClawSweeper PR egg 🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress. Hatch commandComment Hatchability rules:
What is this egg doing here?
|
0beaddc to
6d57d90
Compare
6d57d90 to
17a011b
Compare
17a011b to
eb49667
Compare
|
Merged via squash.
Thanks @medns! |
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Merged via squash. Prepared head SHA: eb49667 Co-authored-by: medns <1575008+medns@users.noreply.github.com> Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com> Reviewed-by: @odysseus0
Summary
GET /sessions/:key/historyand its SSE stream was overly strict, requiringoperator.readexplicitly even for shared-secret (API key) callers, leading to 403 or stream disconnects.Authorization: Bearer <key>) couldn't fetch their own session history, breaking integrations that expect API keys to work for history.resolveTrustedHttpOperatorScopestoresolveOpenAiCompatibleHttpOperatorScopesinsessions-history-http.ts, matching/v1/chat/completionsand other compat endpoints. Also added a new test case and removed the outdated 403 expectations.control-uiandsession-kill) still require strong identity/explicit scopes.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof
Behavior or issue addressed: Fixed
403 missing scope: operator.readand SSE disconnects on/sessions/:key/historyfor standard bearer API keys.Real environment tested: Local development gateway (
pnpm gateway:dev).Exact steps or command run after this patch:
Start gateway locally:
$env:OPENCLAW_SKIP_CHANNELS="1"; $env:OPENCLAW_GATEWAY_TOKEN="test-token-12345"; $env:OPENCLAW_GATEWAY_AUTH_MODE="token"; node scripts/run-node.mjs --dev gatewayFirst, seed a session, then fetch the seeded history using the bearer token via REST:
curl -s -H "Authorization: Bearer test-token-12345" http://127.0.0.1:19001/sessions/agent:main:test-session/history?limit=1Then, establish an SSE connection using the bearer token to verify it stays authorized across transcript updates:
curl -N -H "Authorization: Bearer test-token-12345" -H "Accept: text/event-stream" http://127.0.0.1:19001/sessions/agent:main:test-session/historyEvidence after fix:
Console output for seeded REST fetch:
{"sessionKey":"agent:main:test-session","items":[{"role":"assistant","content":[{"type":"text","text":"this is a seeded real-behavior-proof message"}],"__openclaw":{"id":"msg-1","seq":1}}],"messages":[{"role":"assistant","content":[{"type":"text","text":"this is a seeded real-behavior-proof message"}],"__openclaw":{"id":"msg-1","seq":1}}],"hasMore":false}Console output for SSE connection during a transcript update:
Observed result after fix: The gateway successfully authenticates the bearer token and returns the seeded history payload. The SSE stream successfully establishes and correctly stays authorized (no disconnects) when new message chunks are emitted to the stream.
What was not tested: None.
Root Cause (if applicable)
/sessions/:key/historyincorrectly usedresolveTrustedHttpOperatorScopesinstead ofresolveOpenAiCompatibleHttpOperatorScopes, making it out-of-sync with other compatible endpoints like/v1/chat./sessions/:key/history.sessions-history-http.tsto the compatible API whitelist.Regression Test Plan (if applicable)
src/gateway/sessions-history-http.test.tsUser-visible / Behavior Changes
Callers using a simple
Authorization: Bearer <Token>without thex-openclaw-scopesheader can now successfully read/sessions/:key/historyand establish SSE history streams without receiving a 403 or getting unexpectedly disconnected by the heartbeat check.Diagram (if applicable)
N/A
Security Impact (required)
NoNoNoNoYesYes, explain risk + mitigation: We are intentionally grantingCLI_DEFAULT_OPERATOR_SCOPESto shared-secret API key callers on/sessions/:key/history, to align with other compatible APIs. This is safe as possessing the global API key implies operator access.Repro + Verification
Environment
Steps
/sessions/:key/historywith a validAuthorization: Bearer <key>.Expected
Actual
Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
pnpm test.isStreamStillAuthorized()to ensure stream connections aren't dropped.Review Conversations
Compatibility / Migration
YesNoNoRisks and Mitigations
/sessions/:key/historyendpoint, aligning it perfectly with already-existing compatible routes (/v1/*).