You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Carry OAuth scopes through extraction to the invocation credential
Tool catalogs were compiled with no notion of scope: an operation's
security declaration never survived extraction, and the credential built
for dispatch never carried what the connection's grant covers. A
connection whose grant is narrower than its integration's catalog (the
multi-product Google bundle case) exposed every tool as equally
invocable, and the eventual upstream 403 could not say which scope was
missing versus held.
Extract per-operation security scopes into ExtractedOperation and
OperationBinding (all three compile paths: whole-tree, streamed, and
structure-streamed; optional so stored bindings keep decoding), add
grantedScopes to ToolInvocationCredential sourced from the connection
row's oauth_scope, and use both to annotate scope-insufficient 403s
with the exact required and granted scopes. Advisory-only by design:
scope-string containment needs provider semantics (a broad Google scope
does not textually contain a narrow one), so nothing is blocked locally
and unknown grants fail open.
Refs #1384
// Name the shortfall as precisely as the data allows: the scopes
738
+
// the upstream challenge asked for, else the operation's declared
739
+
// requirement (from the binding; alternatives joined with "or",
740
+
// since each Security Requirement Object is one acceptable set),
741
+
// plus what the connection's grant actually holds. Advisory only —
742
+
// the upstream made the call; this annotation tells the agent/user
743
+
// what to reconnect with.
744
+
constrequired=
745
+
insufficientScope.requiredScopes.length>0
746
+
? insufficientScope.requiredScopes.join(" ")
747
+
: (binding.requiredScopeAlternatives??[])
748
+
.map((alternative)=>alternative.join(" "))
749
+
.join(", or ");
750
+
constgranted=input.credential.grantedScopes;
735
751
returnopenApiAuthToolFailure({
736
752
code: "oauth_scope_insufficient",
737
753
status: result.status,
738
-
message: `The connection "${input.credential.connection}" for "${integration}" is authorized, but its grantdoes not cover the scope this operation requires${required.length>0 ? ` (${required.join(" ")})` : ""}. Re-authenticating with the same grant will return the same error; reconnect with broader access.`,
754
+
message: `The connection "${input.credential.connection}" for "${integration}" is authorized, but its grant${granted&&granted.length>0 ? ` (${granted.join(" ")})` : ""}does not cover the scope this operation requires${required.length>0 ? ` (${required})` : ""}. Re-authenticating with the same grant will return the same error; reconnect with broader access.`,
0 commit comments