From dca260c974add139e5a377378f696e81832c2638 Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Mon, 15 Jun 2026 08:55:19 -0700 Subject: [PATCH] Document v1.15 OAuth consent flow, PATHFINDER_CONSENT_HMAC_KEY env var, and WWW-Authenticate RFC 9728/6750 --- docs/clients/index.html | 4 ++-- docs/config/index.html | 14 ++++++++++++++ docs/deploy/index.html | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/clients/index.html b/docs/clients/index.html index 648a9ad..ee0e82f 100644 --- a/docs/clients/index.html +++ b/docs/clients/index.html @@ -260,7 +260,7 @@

Transport Choices

  • SSE (/sse + /messages) — legacy transport; needed for the claude.ai web connector and older MCP clients.
  • -

    Both transports share the same tools, sessions, and OAuth flow. Clients without a bearer token pass through; invalid bearers are rejected with 401.

    +

    Both transports share the same tools, sessions, and OAuth flow. Clients without a bearer token pass through; invalid bearers are rejected with 401. As of v1.15.1, the WWW-Authenticate challenge on a 401 includes RFC 9728 resource_metadata="…/.well-known/oauth-protected-resource" plus error="invalid_token" per RFC 6750 §3.1 — MCP clients can use the resource_metadata attribute to discover the authorization server.

    Client Configurations

    @@ -295,7 +295,7 @@

    Claude Code

    Claude.ai (Web Connector)

    Settings → Connectors → Add custom connector
    -

    claude.ai uses the legacy SSE transport. Paste your Pathfinder URL into the custom connector dialog, then click Authenticate — the OAuth handshake runs automatically and the connector is approved anonymously.

    +

    claude.ai uses the legacy SSE transport. Paste your Pathfinder URL into the custom connector dialog, then click Authenticate — the OAuth handshake runs automatically. As of v1.15, the handshake shows a consent screen confirming the connector and its redirect target before issuing the auth code; the screen is single-click for the anonymous DCR flow.

    # Streamable HTTP (newer clients) URL: https://your-pathfinder-host/mcp diff --git a/docs/config/index.html b/docs/config/index.html index 03998f0..acfb5e5 100644 --- a/docs/config/index.html +++ b/docs/config/index.html @@ -1615,6 +1615,20 @@

    OAuth endpoints

    + +

    + GET /authorize renders a server-rendered consent screen instead of auto-approving. The screen carries an HMAC-bound nonce; the form posts back to POST /authorize/consent, which re-verifies every bound parameter (client_id, redirect_uri, state, code_challenge, response_type, scope, resource) before issuing an auth code. The final redirect URL is built from the nonce-bound redirect_uri, never the form body — this is the phishing-resistance invariant. +

    +

    + The page is locked down with Content-Security-Policy: default-src 'none'; frame-ancestors 'none', X-Frame-Options: DENY, and Referrer-Policy: no-referrer. No JavaScript, no external resources. +

    +

    + POST /register applies a redirect_uri policy: https-only except for loopback (localhost, 127.0.0.0/8, [::1]); rejects 0.0.0.0/8, RFC 1918, link-local, ULA, IPv4-mapped private addresses, wildcards, userinfo, and fragments. Maximum 2048 chars per URI, 10 URIs per client. +

    +

    + Registered clients are capped at 10,000 total / 100 per-IP with lazy TTL eviction (30 days unused or 7 days never-used-after-registration). Overflow returns 429 (per-IP) or 503 (total) with a Retry-After header. +

    +

    Bearer auth on /mcp and /sse

    diff --git a/docs/deploy/index.html b/docs/deploy/index.html index a6dd6f9..5ec1c3d 100644 --- a/docs/deploy/index.html +++ b/docs/deploy/index.html @@ -299,6 +299,7 @@

    Docker Compose (full stack)

    GITHUB_TOKEN: ${GITHUB_TOKEN:-} GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-} MCP_JWT_SECRET: ${MCP_JWT_SECRET} + PATHFINDER_CONSENT_HMAC_KEY: ${PATHFINDER_CONSENT_HMAC_KEY} PATHFINDER_CONFIG: /app/pathfinder.yaml WORKSPACE_DIR: /data/workspaces PORT: ${PORT:-3001} @@ -356,6 +357,8 @@

    systemd service

    Environment=PORT=3001 Environment=DATABASE_URL=postgresql://pathfinder:secret@localhost:5432/pathfinder Environment=OPENAI_API_KEY=sk-... +Environment=MCP_JWT_SECRET=... +Environment=PATHFINDER_CONSENT_HMAC_KEY=... Environment=PATHFINDER_CONFIG=/opt/pathfinder/pathfinder.yaml Environment=WORKSPACE_DIR=/var/lib/pathfinder/workspaces Restart=always @@ -400,6 +403,7 @@

    Environment Variables

    DATABASE_URLFor search tools-PostgreSQL connection string (with pgvector) MCP_JWT_SECRETRequired in production-HMAC secret for signing OAuth access/refresh tokens. Generate with openssl rand -hex 32. Rotating this invalidates all issued tokens — clients will re-authenticate transparently. In any non-production environment (any NODE_ENV other than production) a random secret is generated per process and logged as a warning. + PATHFINDER_CONSENT_HMAC_KEYRequired in production-HMAC secret for signing the consent-screen nonce on the OAuth /authorize/authorize/consent flow. Generate with openssl rand -hex 32. Comma-separated values are accepted for rotation — the first key signs, all keys verify; rotate by prepending a new key. Rotating invalidates only in-flight consent nonces (10-minute TTL) — issued access/refresh tokens are unaffected. In any non-production environment (any NODE_ENV other than production) a random ephemeral key is generated per process and logged as a warning. OPENAI_API_KEYWhen embedding.provider is "openai" (default)-OpenAI API key for computing embeddings. Not needed for ollama or local providers. GITHUB_TOKENFor private repos-GitHub PAT for cloning private repositories GITHUB_WEBHOOK_SECRETFor webhooks-Secret for validating GitHub webhook payloads