Skip to content

OAuth callback with an expired state returns a bare 403 "Admin only" — no sign the round is gone #5598

Description

@Ninthkitten

Prerequisites

  • I searched open issues and discussions and did not find an existing report of this bug.
  • This is not a security vulnerability.
  • I am running the latest code from the dev branch.

Install Method

Docker

Operating System

macOS (Apple Silicon), Docker Desktop

Steps to Reproduce

  1. Start an OAuth round for a remote MCP server (panel shows the Authorize link).
  2. Restart the container — or simply let 5 minutes pass.
  3. Click the Authorize link that is still on screen and complete the provider login.

Expected Behaviour

The user is told the round expired and what to do about it.

Actual Behaviour

Pending rounds live in process memory (_pending in src/mcp_oauth.py), so a restart clears them — but the panel's Authorize link never greys out or expires visually. A user who clicks a stale link completes a real provider login and lands on:

{"detail":"Admin only"}

This reads as a permissions problem. The truth is "this authorization round no longer exists". We lost an evening to it: the message sent us looking at auth configuration, cookies and admin roles, when the actual fix was simply to press Reconnect and mint a fresh round.

Additional Information

Two suggestions, either would have saved the time:

  1. When a callback arrives with a state that matches no pending flow, return a dedicated page — "This authorization round has expired. Press Reconnect in Odysseus and try again." — instead of falling through to the admin guard's bare 403.
  2. Expire the Authorize link in the UI when its round is gone, so a dead link cannot look live.

Both are small. The second also prevents the failure entirely.

Context: the underlying "cookie does not reach the callback origin" problem is reported separately.

Are you willing to submit a fix?

Yes — the fix described above is running in production here and I can open a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready for reviewDescription complete — ready for maintainer review

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions