Skip to content

[BUG][AUTH]: JWT_AUDIENCE_VERIFICATION=false does not disable issuer validation #1792

@roller100

Description

@roller100

🐞 Bug Summary

Setting JWT_AUDIENCE_VERIFICATION=false disables audience (aud) validation but still requires the iss (issuer) claim. Tokens without an iss claim are rejected with 401 Unauthorized even when this flag is false.


🧩 Affected Component

  • mcpgateway - API
  • mcpgateway - UI (admin panel)
  • mcpgateway.wrapper - stdio wrapper
  • Federation or Transports
  • CLI, Makefiles, or shell scripts
  • Container setup (Docker/Podman/Compose)
  • Other (explain below)

Specific file: mcpgateway/utils/verify_credentials.py (lines ~107-114)


🔁 Steps to Reproduce

  1. Configure Context Forge with JWT_AUDIENCE_VERIFICATION=false
  2. Generate a valid JWT signed with the correct secret but without an iss claim:
    import jwt
    token = jwt.encode(
        {"sub": "user-123", "exp": 9999999999},
        "your-jwt-secret-key",
        algorithm="HS256"
    )
  3. Send request to Context Forge:
    curl -H "Authorization: Bearer <token>" http://localhost:4444/v1/mcp/list_tools
  4. Observe: 401 Unauthorized

🤔 Expected Behavior

When JWT_AUDIENCE_VERIFICATION=false, tokens without iss or aud claims should be accepted (signature and expiration still validated). This would allow integration with third-party systems that issue JWTs without standard claims.


📓 Logs / Error Output

401 Unauthorized: Token is missing the "iss" claim

🧠 Environment Info

Key Value
Version or commit v1.0.0-BETA-1
Runtime Python 3.11
Platform / OS Ubuntu 22.04 (WSL2)
Container Docker Compose

🧩 Additional Context (optional)

Root Cause: In verify_credentials.py, the issuer parameter is always passed to jwt.decode():

decode_kwargs = {
    "key": get_jwt_public_key_or_secret(),
    "algorithms": [settings.jwt_algorithm],
    "options": options,
    "audience": settings.jwt_audience,
    "issuer": settings.jwt_issuer,  # Always passed → always validated
}
payload = jwt.decode(token, **decode_kwargs)

PyJWT behavior: When issuer=<value> is passed, it requires the token to contain a matching iss claim. To skip issuer validation, the parameter must be omitted entirely.

Proposed Fix: Add JWT_ISSUER_VERIFICATION config option:

# Only add issuer if verification enabled
if settings.jwt_issuer_verification:
    decode_kwargs["issuer"] = settings.jwt_issuer

Default True preserves backward compatibility.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingsecurityImproves security

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions