Skip to content

feat: enforce per-tool scopes for scoped agent credentials#14

Open
aurkenb wants to merge 1 commit into
mainfrom
feat/per-tool-scope-enforcement
Open

feat: enforce per-tool scopes for scoped agent credentials#14
aurkenb wants to merge 1 commit into
mainfrom
feat/per-tool-scope-enforcement

Conversation

@aurkenb

@aurkenb aurkenb commented Jun 10, 2026

Copy link
Copy Markdown

What

Adds per-tool scope enforcement for scoped ZenRows agent credentials, while keeping existing full-access API keys working exactly as before.

The ZenRows app now issues scoped, revocable, expiring agent credentials (via OAuth / CLI / OTP / ID-JAG) and exposes a credential-introspection endpoint. This MCP server already passes a presented credential straight through to the Scraper/Browser APIs and already advertises the OAuth discovery metadata — so scoped credentials already flow through. The missing piece was enforcing what each credential is allowed to do.

How

  • src/auth.tsintrospect(apiKey) calls GET /me/credential:
    • 200 → a scoped agent credential; enforce its scopes.
    • 401 (legacy user API key, or revoked/invalid) → no scope data; pass through unchanged (a truly invalid key is still rejected by the Scraper API on use).
    • introspection outage → fail open to pass-through (availability; the API backstops).
    • 60s per-key cache so we don't introspect on every tool call.
  • Scope map: scrape requires scrape:write (+ extract:json when autoparse/css_extractor/outputs is used); all browser_* tools require browser:session_write (a production scope requiring approval) — gated once in the shared tfetch, so all 30+ browser tools are covered without per-handler edits.
  • Denials return an insufficient_scope message pointing to Settings → Agents.
  • ZENROWS_INTROSPECTION_URL config (default https://api.zenrows.com/me/credential).

Compatibility

Fully backward compatible: plain API keys are never scope-gated (introspection returns 401 → pass-through). Only OAuth-issued agent credentials are enforced.

Tests

Adds a node --test harness + test script. npm run typecheck, npm run lint, and npm test (5 tests) all pass. Zero new runtime dependencies.

The ZenRows app now issues scoped, revocable agent credentials. This adds
per-tool scope enforcement to the MCP server, fully backward-compatible with
existing full-access API keys.

- src/auth.ts: introspect a presented credential via GET /me/credential.
  200 → scoped agent credential (enforce scopes); 401 → legacy key / invalid
  (pass through; the Scraper API backstops); outage → fail open. 60s per-key cache.
- scrape requires scrape:write (+ extract:json for structured extraction);
  all browser_* tools require browser:session_write (gated once in the shared
  tfetch). Denials return an insufficient_scope message pointing to Settings → Agents.
- tests/: node --test coverage for the scope logic; add `test` script.
- ZENROWS_INTROSPECTION_URL config; README + .env.example notes.

typecheck + lint + tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant