Skip to content

Commit 6d57d90

Browse files
committed
fix: name shared-secret session history scopes
1 parent 363f8a8 commit 6d57d90

5 files changed

Lines changed: 15 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Docs: https://docs.openclaw.ai
5353
- Auth/OAuth: skip the refresh adapter when a stored OAuth credential has no refresh token so agent turns fail fast on missing-key instead of waiting on the 120s refresh timeout. Thanks @romneyda.
5454
- Auth/Codex: load legacy OAuth sidecar credentials in the embedded runner's secrets-runtime auth loaders so Telegram replies, cron-triggered turns, and other isolated sub-agent lanes can reach the existing #83312 refresh-and-rewrite migration instead of failing with `No API key found for provider "openai-codex"` until the user runs `openclaw doctor`. Thanks @Totalsolutionsync and @romneyda.
5555
- Codex/failover: classify `deactivated_workspace` as a permanent auth failure so configured fallback models can advance when a Codex workspace is deactivated. (#55893) Thanks @litang9.
56+
- Gateway/sessions: allow shared-secret bearer callers to read and stream session history without an explicit scope header. (#81815) Thanks @medns.
5657

5758
## 2026.5.20
5859

docs/gateway/operator-scopes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ own `system.run` exec approval policy.
102102
## Shared-secret auth
103103

104104
Shared gateway token/password auth is treated as trusted operator access for
105-
that Gateway. OpenAI-compatible HTTP surfaces and `/tools/invoke` restore the
106-
normal full operator default scope set for shared-secret bearer auth, even if a
107-
caller sends narrower declared scopes.
105+
that Gateway. OpenAI-compatible HTTP surfaces, `/tools/invoke`, and HTTP session
106+
history endpoints restore the normal full operator default scope set for
107+
shared-secret bearer auth, even if a caller sends narrower declared scopes.
108108

109109
Identity-bearing modes, such as trusted proxy auth or private-ingress `none`,
110110
can still honor explicit declared scopes. Use separate Gateways for real trust

docs/gateway/security/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ Important boundary note:
935935
- On the OpenAI-compatible HTTP surface, shared-secret bearer auth restores the full default operator scopes (`operator.admin`, `operator.approvals`, `operator.pairing`, `operator.read`, `operator.talk.secrets`, `operator.write`) and owner semantics for agent turns; narrower `x-openclaw-scopes` values do not reduce that shared-secret path.
936936
- Per-request scope semantics on HTTP only apply when the request comes from an identity-bearing mode such as trusted proxy auth, or from an explicitly no-auth private ingress.
937937
- In those identity-bearing modes, omitting `x-openclaw-scopes` falls back to the normal operator default scope set; send the header explicitly when you want a narrower scope set.
938-
- `/tools/invoke` follows the same shared-secret rule: token/password bearer auth is treated as full operator access there too, while identity-bearing modes still honor declared scopes.
938+
- `/tools/invoke` and HTTP session history endpoints follow the same shared-secret rule: token/password bearer auth is treated as full operator access there too, while identity-bearing modes still honor declared scopes.
939939
- Do not share these credentials with untrusted callers; prefer separate gateways per trust boundary.
940940

941941
**Trust assumption:** tokenless Serve auth assumes the gateway host is trusted.

src/gateway/sessions-history-http.revocation.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ vi.mock("./http-utils.js", () => ({
4343
const value = req.headers[name.toLowerCase()];
4444
return Array.isArray(value) ? value[0] : value;
4545
},
46-
resolveOpenAiCompatibleHttpOperatorScopes: () => ["operator.read"],
46+
resolveSharedSecretHttpOperatorScopes: () => ["operator.read"],
4747
authorizeScopedGatewayHttpRequestOrReply: async () => ({
4848
cfg: { gateway: { webchat: { chatHistoryMaxChars: 2000 } } },
4949
requestAuth: { trustDeclaredOperatorScopes: true },

src/gateway/sessions-history-http.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
authorizeScopedGatewayHttpRequestOrReply,
2222
checkGatewayHttpRequestAuth,
2323
getHeader,
24-
resolveOpenAiCompatibleHttpOperatorScopes,
24+
resolveSharedSecretHttpOperatorScopes,
2525
} from "./http-utils.js";
2626
import { authorizeOperatorScopesForMethod } from "./method-scopes.js";
2727
import { DEFAULT_CHAT_HISTORY_TEXT_MAX_CHARS } from "./server-methods/chat.js";
@@ -118,9 +118,9 @@ export async function handleSessionHistoryHttpRequest(
118118
return true;
119119
}
120120

121-
// Session history intentionally uses the same shared-secret HTTP trust model as
122-
// the OpenAI-compatible APIs: token/password bearer auth grants default operator scopes
123-
// so simple API key callers can read their own history without a scope header.
121+
// Session history intentionally uses the shared-secret HTTP trust model:
122+
// token/password bearer auth grants default operator scopes so simple API key
123+
// callers can read their own history without a scope header.
124124
const authResult = await authorizeScopedGatewayHttpRequestOrReply({
125125
req,
126126
res,
@@ -129,7 +129,7 @@ export async function handleSessionHistoryHttpRequest(
129129
allowRealIpFallback: opts.allowRealIpFallback,
130130
rateLimiter: opts.rateLimiter,
131131
operatorMethod: "chat.history",
132-
resolveOperatorScopes: resolveOpenAiCompatibleHttpOperatorScopes,
132+
resolveOperatorScopes: resolveSharedSecretHttpOperatorScopes,
133133
});
134134
if (!authResult) {
135135
return true;
@@ -286,7 +286,10 @@ export async function handleSessionHistoryHttpRequest(
286286
if (!currentRequestAuth.ok) {
287287
return false;
288288
}
289-
const requestedScopes = resolveOpenAiCompatibleHttpOperatorScopes(req, currentRequestAuth.requestAuth);
289+
const requestedScopes = resolveSharedSecretHttpOperatorScopes(
290+
req,
291+
currentRequestAuth.requestAuth,
292+
);
290293
return authorizeOperatorScopesForMethod("chat.history", requestedScopes).allowed;
291294
};
292295

0 commit comments

Comments
 (0)