agentsec · Tools · mcpoauth

Offline · stdlib-only · MIT

mcpoauth: detect weak OAuth settings in MCP server metadata in AI agents


Offline auditor for MCP server OAuth discovery metadata.

What it detects

Offline auditor for MCP server OAuth discovery metadata.

Remote MCP servers that require sign-in publish two JSON documents: OAuth 2.0 Protected Resource Metadata (RFC 9728, at /.well-known/oauth-protected-resource) and Authorization Server Metadata (RFC 8414 or OpenID Connect discovery). MCP clients trust those documents to decide where to send users and tokens. mcpoauth reads saved copies and flags settings that the MCP authorization spec and OAuth 2.1 warn against: no PKCE S256, implicit or password grants, cleartext endpoints, tokens in URLs, a resource that does not match the server, broad scopes, and secrets or private keys published by mistake.

Standard library only. It never connects to a network; fetch the documents yourself (for example with curl) and point the tool at the files.

Rules

RuleSeverityWhat it flags
MO01criticalAuthorization server does not advertise PKCE S256 (field missing, or no S256)
MO02highPKCE plain method advertised
MO03highImplicit flow (response_type containing token, or implicit grant)
MO04highResource-owner password grant
MO05highhttp:// endpoint or identifier on a non-loopback host, including redirect_uris / post_logout_redirect_uris; file: / data: / javascript: schemes are flagged as critical
MO06mediumIssuer not https, or has a query or fragment
MO07highbearer_methods_supported includes query (tokens in URLs)
MO08high / mediumProtected resource metadata missing authorization_servers (high) or resource (medium)
MO09highresource does not match --resource-url (medium: has a fragment)
MO10mediumBroad scope advertised (*, all, admin, root, full_access, admin:org, delete_repo, write:packages, ...)
MO11criticalSecret or private key material in metadata (client_secret, private JWK members d/k/...)
MO12critical / highEndpoint on a cloud-metadata or link-local address (critical) or private IP / internal hostname (high)
MO13infoOpen dynamic client registration endpoint (MCP SHOULD support DCR; review confused-deputy risk for proxy servers)
MO14infoInventory of metadata documents found
MO15criticalJWT none algorithm advertised for token or request signing

Usage

python3 mcpoauth.py PATH [PATH ...] [--resource-url URL] [--json | --sarif]
                    [--min-severity LEVEL] [--fail-on LEVEL|none] [--disable RULE ...]
python3 mcpoauth.py --list-rules
# or, from a kit install:
agentsec mcpoauth examples/risky

After pip install of the kit, the same options work as agentsec mcpoauth …. Every tool also accepts --help and almost every tool accepts --json.

Example output

examples/clean shows the same server configured well and produces only the MO14 inventory line.

$ python3 mcpoauth.py examples/risky --resource-url https://mcp.example.com/mcp --min-severity high
CRITICAL  MO01  examples/risky/oauth-authorization-server.json:9  /code_challenge_methods_supported  PKCE methods ['plain'] do not include S256
CRITICAL  MO11  examples/risky/oauth-authorization-server.json:12  /client_secret  metadata contains 'client_secret' value (FAKE…00)
CRITICAL  MO11  examples/risky/oauth-authorization-server.json:13  /jwks/keys/0/k  JWK publishes private member 'k' (FAKE…00)
CRITICAL  MO12  examples/risky/oauth-protected-resource.json:3  /authorization_servers/1  authorization_servers/1 points at a metadata address (169.254.169.254)
HIGH      MO05  examples/risky/oauth-authorization-server.json:2  /issuer  issuer uses cleartext http: http://auth.example.com?tenant=1
HIGH      MO05  examples/risky/oauth-authorization-server.json:3  /authorization_endpoint  authorization_endpoint uses cleartext http: http://auth.example.com/authorize
HIGH      MO03  examples/risky/oauth-authorization-server.json:7  /response_types_supported  implicit flow returns tokens in the browser redirect; OAuth 2.1 removes it
HIGH      MO04  examples/risky/oauth-authorization-server.json:8  /grant_types_supported  password grant lets a client collect user credentials; OAuth 2.1 removes it
...

SARIF and CI

agentsec mcpoauth path/to/input --sarif > mcpoauth.sarif

# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
  with:
    tools: mcpoauth
    paths: path/to/input
    fail-on: high

SARIF 2.1.0 can be uploaded to GitHub code scanning. The composite action writes per-tool reports under agentsec-sarif/ by default.

OWASP mapping

Heuristic mapping of what this tool helps a reviewer look for. Not an official OWASP endorsement, and not complete coverage of any item. Sources: OWASP Top 10 for LLM Applications 2025 and OWASP Top 10 for Agentic Applications 2026.

Known limits

validate JWKS signatures, or test the live flow (redirect URI checks, token audience validation, consent screens). Those need a dynamic test.

nor standard authorization-server keys is ignored.

only when you pass --resource-url.

(.internal, .local, .corp, ...); it does not resolve DNS.

Full source, tests, and fixtures: tools/mcpoauth/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.