Skip to content

OAuth Clients and Scopes

Jm Rohmer edited this page Jul 18, 2026 · 4 revisions

OAuth Clients and Scopes

This page summarizes how OAuth client configuration should work for Claude, ChatGPT, and other MCP clients. It deliberately uses placeholders only.

Canonical Scopes

Since #450, the server advertises exactly two canonical scopes:

read
write

write implies read — there is no longer a separate admin/build tier; every tool that used to require site.admin now just requires write.

Every scope string from the older four-tier model (content.read, content.write, site.admin, system.admin) and a few other historical aliases (mcp, reader, admin, system_admin, siteadmin) are still accepted, but purely as compatibility aliases that normalize to read or write — none of them are advertised as a recommended or distinct scope anymore. See Scope and Tool Matrix and docs/mcp-contract.md §6.12 for the exact alias table.

Scope Tiers

Tier Meaning
anonymous safe public discovery and limited read-only tools, no scope required at the ACL layer
read no scope required beyond holding any valid token (no client secret needed to obtain one) — full read surface, including drafts; every Anonymous tool plus all richer read tools
write requires a registered OAuth client (secret + oauth-clients.yaml entry) — full read surface plus create/update/delete content, build/site-ops, and integrity/diagnostic tools

Correction (2026-07-18, #498). "No auth at all" in the table above describes the tool-scope ACL, not whether /mcp itself requires a bearer token. Production runs with OAuth enabled, and every /mcp request without one gets a 401 challenge regardless of tool tier — see Pitfall Anonymous vs Authenticated Server. read is obtained via self-serve DCR + PKCE, not a bearerless request.

Host-Local Client Registry

OAuth clients can be imported from:

/etc/mcp-hugo-server-go/oauth-clients.yaml

Example shape:

clients:
  - id: claude-admin
    secret: REPLACE_WITH_A_LONG_RANDOM_SECRET
    scopes: ["read", "write", "admin"]
    redirect_uris:
      - "https://claude.ai/*"
    enabled: true

  - id: chatgpt-write
    secret: REPLACE_WITH_A_LONG_RANDOM_SECRET
    scopes: ["read", "write"]
    redirect_uris:
      - "https://chatgpt.com/connector/oauth/*"
    enabled: true

Rules:

  • Keep this file outside the Git repository.
  • Restrict permissions to the service user/root as appropriate.
  • Never log or paste real client secrets.
  • Import should upsert clients, not delete missing clients by default.
  • Redirect URI patterns must be strict: HTTPS scheme, exact host, and exact path or explicit path-prefix pattern.

Redirect URI Policy

Allowed examples:

https://claude.ai/*
https://chatgpt.com/connector/oauth/*

Rejected examples:

http://claude.ai/callback
https://claude.ai.evil.example/callback
https://evil.example/callback

Dynamic Client Registration

Dynamic Client Registration can help clients such as Claude.ai and ChatGPT connect without manual pre-registration. It must still be bounded by server-side policy:

  • Unknown clients must not get write/admin scopes by default.
  • Known redirect URI patterns may define the maximum allowed scope.
  • Requested scopes can be downscoped, but must never be escalated.
  • DCR-created clients must be persisted if clients are expected to survive restarts.

Debug Checklist

When Claude or ChatGPT authenticates but sees the wrong tools:

curl -sk https://mcp.arleo.eu/.well-known/oauth-authorization-server | jq .
curl -sk https://mcp.arleo.eu/.well-known/oauth-protected-resource | jq .
ssh hugo-vm 'journalctl -u mcp-hugo-server-go -n 120 --no-pager'

Look for:

  • client ID
  • granted scope
  • redirect URI match result
  • resource/audience value
  • whether tools/list was called with or without Authorization

Do not log access tokens, refresh tokens, authorization codes, or client secrets.

Clone this wiki locally