Scoped admin OAuth DCR endpoints ignore token_teams restrictions and expose global registered clients
Confidence Note
This report is based on source analysis of version 1.0.4 and I have not yet dynamically validated it against a live instance.
Summary
get_current_user_with_permissions() propagates token_teams into the authenticated user context, and other sensitive admin endpoints explicitly reject narrowed admin sessions when token_teams is not None. However, the OAuth DCR management endpoints only call _require_admin_user(), which checks is_admin but ignores token_teams.
As a result, a scoped admin token appears able to list, retrieve, and delete global RegisteredOAuthClient records outside its allowed team scope.
Impact
If this behavior is reachable in deployment, an administrative token that is intentionally narrowed to specific teams may still access the global OAuth dynamic client registration management surface. That would allow cross-scope visibility into registered clients and may also allow deletion of client registrations outside the token's intended authorization boundary.
Affected Endpoints
GET /registered-clientsGET /registered-clients/{gateway_id}DELETE /registered-clients/{client_id}
Technical Details
The RBAC layer includes token_teams in the authenticated user context:
mcpgateway/middleware/rbac.py:443-455
The helper used by the OAuth DCR management endpoints only verifies is_admin:
-...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| mcp-contextforge-gateway PyPI | < v1.0.8 | v1.0.8 |
Details and references
### Confidence Note This report is based on source analysis of version `1.0.4` and I have not yet dynamically validated it against a live instance. ### Summary `get_current_user_with_permissions()` propagates `token_teams` into the authenticated user context, and other sensitive admin endpoints explicitly reject narrowed admin sessions when `token_teams is not None`. However, the OAuth DCR management endpoints only call `_require_admin_user()`, which checks `is_admin` but ignores `token_teams`. As a result, a scoped admin token appears able to list, retrieve, and delete global `RegisteredOAuthClient` records outside its allowed team scope. ### Impact If this behavior is reachable in deployment, an administrative token that is intentionally narrowed to specific teams may still access the global OAuth dynamic client registration management surface. That would allow cross-scope visibility into registered clients and may also allow deletion of client registrations outside the token's intended authorization boundary. ### Affected Endpoints - `GET /registered-clients` - `GET /registered-clients/{gateway_id}` - `DELETE /registered-clients/{client_id}` ### Technical Details The RBAC layer includes `token_teams` in the authenticated user context: - `mcpgateway/middleware/rbac.py:443-455` The helper used by the OAuth DCR management endpoints only verifies `is_admin`: - `mcpgateway/routers/oauth_router.py:192-203` The affected endpoints rely on that helper and do not reject narrowed admin sessions: - `mcpgateway/routers/oauth_router.py:988-1033` - `mcpgateway/routers/oauth_router.py:1040-1084` - `mcpgateway/routers/oauth_router.py:1093-1136` By contrast, another sensitive admin surface explicitly blocks narrowed tokens by checking whether `token_teams` is not `None`: - `mcpgateway/routers/tokens.py:624-633` The registered OAuth client objects appear to be stored globally and are queried directly without team scoping in these endpoints: - `mcpgateway/db.py:5317-5335` This combination suggests that a scoped admin session may enumerate or delete registered OAuth clients beyond its intended scope. ### Reproduction Idea Based on source analysis, the likely reproduction path is: 1. Obtain an authenticated admin token or session that is intentionally narrowed through `token_teams`. 2. Call `GET /registered-clients`. 3. Observe whether the response includes globally registered OAuth clients outside the teams allowed by the token. 4. Call `GET /registered-clients/{gateway_id}` for a gateway outside the narrowed scope. 5. Attempt `DELETE /registered-clients/{client_id}` against a registration outside the token's allowed team scope. 6. If successful, this confirms cross-scope authorization bypass. ### Root Cause The authorization model distinguishes between full admin access and narrowed admin access, but the OAuth DCR management endpoints enforce only the admin bit and ignore the narrowing metadata carried in `token_teams`. ### Suggested Remediation - Apply the same un-narrowed admin check used in other sensitive endpoints to the OAuth DCR management endpoints. - Enforce explicit scope validation before listing, retrieving, or deleting registered OAuth clients. - Add tests covering narrowed admin tokens against all DCR management routes. - Review other admin-only endpoints for similar `is_admin`-only checks that ignore `token_teams`. ### Code References - `mcpgateway/middleware/rbac.py:443-455` - `mcpgateway/routers/oauth_router.py:192-203` - `mcpgateway/routers/oauth_router.py:988-1033` - `mcpgateway/routers/oauth_router.py:1040-1084` - `mcpgateway/routers/oauth_router.py:1093-1136` - `mcpgateway/routers/tokens.py:624-633` - `mcpgateway/db.py:5317-5335`
- CVSS 3.1
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
- Severity from
- GitHub (reviewed advisory)