Skip to content

[connectors] dcr-oauth2 — discovery + dynamic client registration + PKCE for user-named endpoints #180

Description

@serge-ivo

Design: docs/connector-auth.md.

The gap

The connector auth model (api-key | oauth2 | app | none) assumes the operator knows the remote system at build time. oauth2 requires secretRef resolved from operator env — a pre-registered, confidential client.

That breaks for the first connector where a subscriber names the endpoint at runtime (outbound MCP, mcp_url as a per-instance setting). The operator cannot pre-register with a server they have never heard of, so secretRef is unfillable: there is no value that could go in it.

Consequence: the mcp connector shipped auth:"token" — a vault bearer the user pastes in — which the first real target (FreeWebStore) explicitly documents as "a compatibility fallback" alongside "do not ask users to paste access tokens."

This also blocks #143 Phase 3 (creator-supplied manifests): a creator cannot add a client secret to the operator env, so any creator connector needing OAuth requires an operator deploy — exactly what #143 exists to abolish.

The fix

Add auth.type: "dcr-oauth2". The connector declares nothing about the remote server; the resource URL is the input.

Step Spec Removes
Discover auth server from resource RFC 9728 protected-resource metadata hardcoded authUrl/tokenUrl
Discover endpoints RFC 8414 AS metadata manifest endpoint config
Register the client at runtime RFC 7591 dynamic client registration secretRef, pre-registration
Authorize without a secret PKCE S256, public client the client secret entirely

This is the stack MCP mandates. Verified live against FreeWebStore:

POST https://agent.freewebstore.online/register
 -> 201 {"client_id":"6bda1761-...","redirect_uris":["https://api.proagentstore.online/v1/connectors/mcp/oauth/callback"]}

PAGS registered itself, for its own callback, with no operator involvement and no secret.

Scope

  • Discovery chain: resource URL -> RFC 9728 -> RFC 8414, through safeFetch (SSRF-guarded, https-only).
  • DCR client registration cached per (user, resource origin) — one registration per server per user, reused across instances; envelope-encrypted like every other credential.
  • PKCE S256 authorize + callback, reusing the HMAC-signed state in lib/connector-oauth.ts.
  • Grant storage keyed by resource origin, not connector id alone (a user may connect several MCP servers).
  • connectorClient dispatch for the new type; mcp connector switches to it when discovery finds an auth server, falling back to vault bearer only when it does not.

Acceptance

  • A subscriber can connect an arbitrary OAuth-protected MCP server with no operator action and no pasted token.
  • Registration is performed once per (user, origin) and reused.
  • A server offering no auth metadata still works via the existing vault-bearer path.
  • Discovery cannot be pointed at internal addresses (SSRF guard covers the metadata fetches).
  • Tests cover: discovery, DCR, PKCE round-trip, cached registration reuse, and the no-metadata fallback.

Notes

Getting connected is necessary but not sufficient for unattended chains — see the companion issue on credential survivability.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend / Worker / API workconnectorsConnector + tool framework

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions