close
Skip to content

OAuth consent-URL allowlist is 7 hardcoded endpoints — no operator escape hatch for other IdPs #1341

Description

@pepmach

What

The OAuth consent-URL safety gate introduced in #1328 (oauth_url_contains_credential in src/kiro_crew/security.py) trusts an exact-match allowlist of hardcoded (host, path) pairs. Any identity provider outside that frozen set has its consent URL rejected, so the Connections banner shows a generic authentication failure with no operator escape hatch short of a code release.

Providers currently excluded include:

  • Okta (<tenant>.okta.com/oauth2/v1/authorize)
  • Auth0 (<tenant>.auth0.com/authorize)
  • Self-hosted OIDC of any kind
  • Tenant-scoped Microsoft — only login.microsoftonline.com/common/oauth2/v2.0/authorize is listed, but many organisation app registrations require /{tenant}/oauth2/v2.0/authorize, which exact-path matching excludes

Two related specifics worth recording rather than rediscovering:

  1. Non-allowlisted providers do not merely miss the exemption — they then trip the ≥200-character query heuristic, because real consent URLs routinely exceed it. So the failure mode is an unhelpful "authentication failed", not a clear "unsupported provider".
  2. fix(security): stop redacting OAuth consent URLs without weakening secret detection #1328 removed a previously-passing corpus case (a generic id.example-idp.com long-state URL) from test/oauth_url_corpus.py, because the narrowed gate no longer passes it. That deletion documents the regression rather than hiding it, but the case should come back once the allowlist can be extended.

Why

The narrowing was deliberate and is the safe direction: it closes a real exfiltration channel where a state= parameter on an attacker-controlled host could carry a credential past redaction and render as a clickable Authorize link. It also fails closed — an unsupported provider degrades to "cannot authenticate", never to a leaked secret.

There is no impact on the current launch set: M1 ships exactly the six registry providers (notion, github, linear, atlassian, stripe, vercel), all allowlisted. The cost lands the moment anyone connects an MCP server fronted by their own IdP — which, for an open MCP ecosystem, is a matter of when rather than if. At that point the only remedy is a KiroCrew release, which is the wrong shape for what is really operator configuration.

Additional Context

Raised by the advisory design review on #1328 (Design-Verdict: CONCERNS), and deliberately deferred with the decision to ship narrow for M1 rather than grow the PR.

Suggested direction from that review, recorded so the design intent is not lost:

  • Add an operator-owned allowlist extension using the repo's existing keystone pattern — a file fenced like computer_use.json so the agent cannot widen its own trust boundary, while an operator can add an IdP without waiting for a release.
  • Reject non-allowlisted endpoints with a message that names the endpoint and the extension path, so users can tell "unsupported IdP" apart from "tampered URL".
  • Restore the generic-IdP corpus case in test/oauth_url_corpus.py once the extension exists.

Tracked in the design doc under Workstream E / open items so this does not live only in an issue.

Related: #1328 (the narrowing), #1287 (provider registry), #1229 (Connections page).

Metadata

Metadata

Assignees

Labels

area: dashboardDashboard UI and its backend handlersclaimedAn automated fix is in progressenhancementNew feature or requestfollow-upDeferred work split out of a merged PRsecurityHardening, trust boundary, or sandbox escape

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions