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.

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