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
fix(auth): authorize API keys solely by their own allowlist (no owner-role widening) (#4180)
* fix(auth): authorize API keys solely by their own allowlist
A key's owner role no longer widens it: an API key is authorized strictly by its
stored `permissions`. A key an admin scopes to e.g. ORGANIZATION_GET cannot call
any other tool — the privilege escalation reported by an agent run (a read-only
key was minting admin keys via API_KEY_CREATE).
- `AccessControl.checkResource` now dispatches to one of two self-contained
codepaths by principal type: an **API-key** principal is decided SOLELY by the
key's allowlist (`checkApiKeyAccess` → `checkApiKeyPermission`) — no role, no
admin/owner bypass, no basic-usage membership floor, no Better Auth; a
**member** principal (session / MCP OAuth / mesh JWT) keeps the existing
membership-floor + role-bypass + Better Auth path (`checkMemberAccess`). The
flag lives on `boundAuth.isApiKeyPrincipal`, so REST and MCP share one signal.
- `createBoundAuthClient.hasPermission` mirrors the split (API key →
`checkApiKeyPermission` before any role logic).
- Remove the "default permissions" concept. `API_KEY_CREATE` now requires
`permissions`, and the three internal minters that relied on the default pass
an explicit scope: the per-run agent MCP key and the dev-link key act as the
user (`{ "*": ["*"] }`), the org-fs mount uses `{ self: ["*"] }` (its file ops
are covered by that scope). "A key without a scope" no longer exists.
A key is therefore exactly its allowlist — not allowlist ∪ basic-usage. Internal
keys carry a wildcard so they are unaffected; user-scoped keys are tightened to
precisely what they were scoped to. Browser sessions, MCP OAuth, and mesh JWTs
are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(e2e): an API key is capped at its allowlist regardless of owner role
Black-box HTTP regression: an admin-owned key scoped to AUTOMATION_LIST is denied
MONITORING_STATS, API_KEY_CREATE, and a basic-usage tool outside its allowlist
(403) — a key is a capability, not a member. A wildcard key keeps full access;
API_KEY_CREATE without `permissions` is rejected (400).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments