fix(connections): discover Linear MCP OAuth endpoints - #11078
fix(connections): discover Linear MCP OAuth endpoints#11078cryppadotta wants to merge 3 commits into
Conversation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Greptile SummaryThe PR changes Linear MCP OAuth from fixed classic API endpoints to protected-resource and authorization-server discovery, including dynamic client registration. It also enables Linear in the direct OAuth wizard and adds regression coverage for discovery and endpoint precedence.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/shared/src/app-definitions/linear.json | Removes the classic Linear OAuth endpoint pair so the broker discovers the MCP authorization server and performs dynamic client registration. |
| server/src/tests/tool-access-service.test.ts | Adds regression coverage for complete manifest-pair precedence and discovery-first Linear OAuth. |
| ui/src/pages/apps/app-connect-policy.ts | Adds Linear to the direct MCP OAuth connection allowlist. |
| ui/src/pages/apps/Browse.tsx | Generalizes gallery connection links to use the direct OAuth policy for both Notion and Linear. |
| doc/connections/CONNECTOR-PLAYBOOK.md | Documents Linear’s discovery and registration flow, endpoint behavior, wizard limitations, and QA expectations. |
Reviews (3): Last reviewed commit: "fix(apps): enable allowlisted OAuth tile..." | Re-trigger Greptile
Co-Authored-By: Paperclip <noreply@paperclip.ing>
cryppadotta
left a comment
There was a problem hiding this comment.
Security review verdict: changes requested (OWASP API: Unsafe Consumption of APIs; STRIDE: Spoofing / Information Disclosure; principles: Complete Mediation, Fail Securely).
Blocking finding — OAuth AS mix-up / unattested token endpoint fallback
discoverOAuthEndpoints builds candidates from the configured MCP serverUrl, but after the RFC 9728 protected-resource candidates it also tries /.well-known/oauth-authorization-server and /.well-known/openid-configuration directly. Consequently, when Linear's protected-resource metadata is missing, malformed, or returns no usable endpoints, startOAuth can still accept an AS document and persist/send the authorization code + PKCE verifier to its token_endpoint, even though the protected resource never named that authorization server.
PoC regression shape:
https://mcp.linear.app/.well-known/oauth-protected-resource/mcpand the origin-level protected-resource URL return 404/unusable metadata.https://mcp.linear.app/.well-known/oauth-authorization-serverreturns authorization, token, and registration endpoints (the token endpoint can be another public origin).- Current code completes DCR/start and later posts the code/verifier to that token endpoint. Expected: discovery fails before DCR/authorization because there is no RFC 9728 attestation.
Blast radius: a compromised/misconfigured discovery surface (or equivalent upstream control) can redirect OAuth code material and PKCE verifier to an unattested AS. Exploitability is low-to-moderate because HTTPS/provider-origin control is required; impact is high for the connecting Linear identity and potentially its authorized workspace data. Overall: medium.
Required fix:
- For gallery MCP OAuth entries without a complete trusted manifest pair, require successful RFC 9728 resource metadata rooted at the configured protected resource.
- Follow only an
authorization_serversissuer advertised by that resource; do not fall through to bare AS/OIDC discovery. - Validate the protected-resource
resourcevalue against the configured resource when present, and validate returned ASissueragainst the advertised issuer when present. - Add a negative regression test proving unavailable/unusable resource metadata never reaches DCR or a token endpoint. Keep the new positive Linear test and complete-pair precedence test; they correctly pin both requested branches and would fail if the wrong Linear pair were re-added.
Other review areas passed:
- Complete manifest pair remains authoritative and skips discovery.
- Linear's manifest omission drives RFC 9728 first in the happy path.
- PKCE uses S256 with a random verifier; state is expiring, one-time, and actor/session-bound.
- DCR requests a public client and validates the exact redirect URI/grants/response type/auth method returned.
- Callback URI is server-derived rather than request-controlled, and non-GET OAuth redirects are rejected.
- No access tokens, client secrets, or live credentials appear in the diff, fixtures, or connector playbook.
Residual risk after the fix: a genuinely attested malicious/compromised AS can still choose its own endpoints; that is inside the protected resource's delegated trust boundary and should remain constrained by SSRF checks and TLS.
UX review (UXDesigner, PAP-16806) — changes requested: one gating UI gap, core flow verified liveRan this branch ( Verified working: Gating (PAP-16826): the flow is unreachable by clicking. Filed separately, not gating (PAP-16827, pre-existing broker behavior shared with notion): provider-cancel dead-ends at raw JSON on |
Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
@greptile review |
Thinking Path
Linked Issues or Issue Description
What happened?
The Linear MCP catalog entry supplied
linear.appauthorization andapi.linear.apptoken endpoints. The broker treated this complete pair as authoritative and did not discover the authorization server declared bymcp.linear.app.Expected behavior
Paperclip must start from Linear's RFC 9728 protected-resource metadata. It must then use the RFC 8414 authorization-server metadata at
mcp.linear.appand register a public client through DCR when no configured client exists.Steps to reproduce
Paperclip version or commit
masterbefore this pull request.Deployment mode
Local dev and self-hosted server.
Related research: https://github.com/paperclipai/paperclip-content/pull/15
What Changed
Verification
pnpm exec vitest run packages/shared/src/app-definitions.test.ts ui/src/pages/apps/app-connect-policy.test.ts server/src/__tests__/tool-access-service.test.ts -t 'AppDefinition catalog|app connect policy|endpoint pair|discovers Linear'pnpm check:token-gatespnpm -r typecheckenv -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY -u AWS_SESSION_TOKEN pnpm test:runpnpm buildgit diff --checkRisks
Model Used
OpenAI Codex with GPT-5. The hosted runtime does not expose a more specific model build ID or context-window size. The model used reasoning, tool use, and code execution.
Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template#NNN/github.com/paperclipai/paperclipURLs)docs/...,fix/...) and contains no internal Paperclip ticket id or instance-derived details