feat: OAuth client scope assignment in admin panel

- Create client: ctxd.read / ctxd.write checkboxes
- Client list: show scopes, edit via PATCH /oauth/clients/:id
- Authorize grants intersection of client allowed scopes and request
- CLI oauth-client-create --scope; DCR default ctxd.read ctxd.write
This commit is contained in:
2026-06-25 13:13:25 +00:00
parent 87f02eb4d1
commit 1c9d8f7648
3 changed files with 199 additions and 12 deletions
+8 -2
View File
@@ -351,6 +351,10 @@ def cmd_oauth_client_create(args):
client = store.register_client({
"client_name": args.name or "Claude MCP Client",
"redirect_uris": redirects,
"scopes": getattr(args, "scopes", None) or (
[s for s in (args.scope or "").split() if s]
or ["ctxd.read", "ctxd.write"]
),
})
issuer = (cfg.oauth_issuer or "").rstrip("/") or "https://ctxd.cubecraftcreations.com"
print(json.dumps({
@@ -358,7 +362,8 @@ def cmd_oauth_client_create(args):
"client_secret": client["client_secret"],
"client_name": client.get("client_name"),
"redirect_uris": client.get("redirect_uris"),
"connector_url": f"{issuer}/readonly/sse",
"scope": client.get("scope"),
"connector_url": f"{issuer}/mcp",
"authorization_server": issuer,
"note": "Claude usually registers via POST /oauth/register automatically; save client_secret now — it is not shown again.",
}, indent=2))
@@ -371,7 +376,7 @@ def cmd_oauth_client_list(args):
cfg = CtxConfig.from_home(args.home)
store = OAuthStore(cfg)
for c in store.list_clients_public():
print(f"{c.get('client_id')} {c.get('client_name', '')} redirects={c.get('redirect_uris')}")
print(f"{c.get('client_id')} {c.get('client_name', '')} scope={c.get('scope', '')} redirects={c.get('redirect_uris')}")
def cmd_oauth_client_revoke(args):
@@ -559,6 +564,7 @@ def build_parser() -> argparse.ArgumentParser:
sp.set_defaults(func=cmd_oauth_client_create)
sp.add_argument("--name", "-n", default="Claude MCP Client", help="Client display name")
sp.add_argument("--redirect-uri", action="append", dest="redirect_uri", help="Redirect URI (default: Claude MCP callback; repeat for multiple)")
sp.add_argument("--scope", default="ctxd.read ctxd.write", help="Allowed scopes (space-separated: ctxd.read ctxd.write)")
sp.add_argument("--home")
# oauth-client-list