Skip to content

OAuth account-token delegation: five groups non-selectable despite explicit first-party CLI consent #433

Open

Description

@3N15

Summary of the issue

Could you clarify the account-token delegation requirements for the first-party Cloudflare CLI OAuth client?

An accepted account membership has Super Administrator - All Privileges. Connected Applications independently shows all nine requested CLI permissions, including Account API Token Provisioning. The OAuth token returns this exact scope set:

offline_access
memberships.read
account_api_tokens:create
workers-scripts.write
query-cache.read
access.read
access-acct.read
zone.read
dns.read
workers-routes.read

The first-party client ID is cbca97e7-c331-4cdd-8fd8-e25a451b98bf. An authenticated GET /accounts/{account_id}/tokens/permission_groups still returned:

Permission group Resource is_selectable
Workers Scripts Write account false
Hyperdrive Read account false
Access: Apps and Policies Read account false
Access: Organizations, Identity Providers, and Groups Read account false
DNS Read zone false
Zone Read zone true
Workers Routes Read zone true

These observations were refreshed on 2026-10-27 at 11:57:44 UTC. The actual profile is explicitly selected; source inspection ruled out an environment/default-token fallback. Read-only API/native preflight with that issuer succeeds for the corresponding resources.

The authenticated owner dashboard allows all seven exact groups. Its official JSON Payload matches the reviewed account/zone resource bounds, permission-group IDs and expiry. One separately approved owner-dashboard creation with those policies succeeded. Its replacement token is operational and will not be changed for this investigation.

Expected behaviour

Cloudflare's October 1 announcement says account-token permissions are bounded by both the creator's membership and OAuth scopes. Could the authorization team explain:

  1. Which additional underlying scope(s), if any, are required for the five groups above?
  2. If the listed scopes should suffice, is there a scope-to-permission mapping discrepancy?
  3. What is the safe authoritative reconciliation path for an earlier failed creation whose complete rejection details are unavailable?

The permission-group documentation correctly says creation is authoritative. One earlier exact-policy POST, at 2026-10-27T10:28:30.735Z, returned HTTP 400 / error code 400. Our previous adapter unfortunately discarded the detailed rejection envelope and original request correlation ID. We therefore do not claim that the catalog flags caused the POST failure. That attempt remains quarantined and has never been retried. Empty inventories and absent accessible audit entries were not treated as proof of non-issuance.

Version of cf used

The cf binary was not invoked. This is an API/OAuth delegation clarification using the published first-party CLI client through @alchemy.run/cloudflare@0.0.0-beta.84 and direct bounded REST reads. Please route to the account-token/OAuth authorization owners if this is outside this repository's scope; this report does not assert a cf command implementation bug.

System information and package versions

macOS; Bun 1.4.3; Alchemy core and Cloudflare provider beta.84; Effect 4.0.6. Only read-only follow-up diagnostics were performed.

Reproduction (optional)

With the explicitly consented first-party CLI OAuth principal, read GET /accounts/{account_id}/tokens/permission_groups and compare the seven rows above. No further token creation was attempted. The successful owner-dashboard creation used the same exact seven policies and expiry; that operational token is preserved.

Relevant error logs (optional)

A step-up for an older Cloudflare MCP connection initially requested account-api-tokens.write together with its existing scope set. The authorization page refused the whole request with:

Invalid Scope
Unknown OAuth scope: workers-scripts.bind
Error code: invalid_scope

Comparison of the actual requested list (385 scopes including protocol scopes) against the live public catalog (401 entries) found only workers-scripts.bind unsupported. Removing only that unsupported entry allowed a fresh OAuth login to complete. A newly connected official Cloudflare plugin can now read the permission-group endpoint with HTTP 200; its separate grant reports the Access/DNS groups selectable, but Workers Scripts Write and Hyperdrive Read still non-selectable. These are different OAuth grants, not proof of the first-party CLI failure's cause.

No token values, account/zone IDs, callback codes, private application code or private screenshots are included in this public request. No additional token issuance, permission mutation or production change is requested.

Activity

  1. 3N15 commented on Oct 27, 2026

    @3N15
    Author

    Follow-up results, 2026-10-27 (UTC): the separate MCP use path now passes login, natural token refresh, reads and isolated Worker writes. The first-party CLI account-token provisioning question remains open.

    • After rebuilding the older MCP connection with the renewed login, the previously refused permission-group request returns HTTP 200. Nine functional GETs covering the target zone/account identity, Workers, Hyperdrive, Access apps/organization, DNS, routes and membership all pass.
    • A uniquely marked, empty Worker with workers.dev and preview URLs disabled accepted creation, an unpublished module-version upload, version readback, metadata editing and deletion, all with HTTP 200. No version was deployed or invoked. The temporary Worker and version were removed; the pre-existing Worker metadata matched before and after.
    • The MCP access token expired naturally at 20:47:55.838 UTC. Immediately before the next native tool call, the original stored token was confirmed unchanged and expired. The call at 20:48:17 UTC succeeded without interactive login. Private keychain metadata then confirmed changed access and refresh tokens, a new 21:48:17 UTC expiry, and unchanged scopes, client and resource identity. We did not edit expiry, call the token endpoint manually or write credentials directly.
    • A second isolated Worker create/edit/read/delete cycle at 20:49 UTC, using the refreshed connection, also passed with HTTP 200 and complete cleanup.

    These tests used the MCP OAuth grant, not the operational Account API token or the separately consented first-party CLI issuer. They establish MCP use authority, not equivalent token-delegation authority, and do not establish the cause of the earlier CLI HTTP 400.

    The original unknown creation attempt has not been retried or reclassified. The operational Account API token, its policy and the production application remain unchanged.

    Could the account-token/OAuth authorization owners clarify the underlying requirements for the five non-selectable groups under the exact first-party CLI scopes in the initial report, and the safe authoritative reconciliation path for that earlier unknown outcome? We are keeping automatic account-token issuance blocked pending that clarification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions