Skip to content

[BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189

@childrenofaqsa

Description

@childrenofaqsa

Here’s the updated bug issue content with the added case included in the Steps to Reproduce:

🐞 Bug Summary

Users who belong to any team cannot see public tools owned by other teams in the list tools API, and filtering by a different team than the token’s team ID returns 403—even when the user is a member of that team.


🧩 Affected Component

Select the area of the project impacted:

  • 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)

🔁 Steps to Reproduce

  1. Create Team A and Team B.
  2. Add the same user to both Team A and Team B.
  3. Issue a token for that user where the first team in the token is Team A.
  4. Call list tools API with team_id=Team B and observe 403 due to team-id mismatch with token.
  5. Create a public tool owned by Team B.
  6. Call list tools API without a team filter and observe Team B public tool is missing.

🤔 Expected Behavior

Public tools should be visible to all users regardless of team membership, and filtering by another team should allow access if the user is a member of that team—even if the token’s “first team” differs.


📓 Logs / Error Output

{
"message": "Access issue: This API token does not have the required permissions for this team."
}


🧠 Environment Info

You can retrieve most of this from the /version endpoint.

Key Value
Version or commit 1.0.0-BETA-1
Runtime Python 3.13
Platform / OS Windows 10.0.26100
Container none

🧩 Additional Context (optional)

in mcpgateway/auth.py: token parsing returns only the first team from the JWT payload

team_id = payload.get("teams")[0] if payload.get("teams") else None
if isinstance(team_id, dict):
team_id = team_id.get("id")
, via, so in mcpgateway/main.py the mismatch check at team_id vs token_team_id only considers that first team and ignores any other teams in the token:
token_team_id = getattr(request.state, "team_id", None)
# Check for team ID mismatch
if team_id is not None and token_team_id is not None and team_id != token_team_id:
return JSONResponse(
content={"message": "Access issue: This API token does not have the required permissions for this team."},
status_code=status.HTTP_403_FORBIDDEN,
)

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafebugSomething isn't workingpythonPython / backend development (FastAPI)rbacRole-based Access ControlsecurityImproves security

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions