LibreChat/packages/api
Danny Avila d1573a8493
🔒 fix: Validate MCP OAuth Protected Resource Metadata binding (#12755)
* 🔒 fix: Validate MCP OAuth Protected Resource Metadata binding (GHSA-gvpj-vm2f-2m23)

RFC 9728 §3.3/§7.3 requires clients to verify that the `resource` identifier
advertised by an OAuth Protected Resource Metadata document matches the URL
used to fetch it. Without this check, a malicious MCP server can serve
metadata pointing at a legitimate server's `authorization_servers`, causing
LibreChat to obtain an access token for the real server and send it to the
attacker in subsequent API calls.

Validation happens at discovery time so the entire metadata document is
discarded on mismatch — `authorization_servers` on a spoofed document is
equally untrustworthy and is the primary theft vector in the PoC.

Uses the MCP SDK's own `checkResourceAllowed` (origin + path-prefix) for
semantic parity with `selectResourceURL`, a SDK code path LibreChat bypasses.
This is looser than RFC-strict equality (handles common cases like
`/mcp/sse` server vs `/mcp` advertised resource, or trailing-slash
normalization) while still rejecting cross-origin spoofs and same-origin
sibling-path confusion.

* 🛡️ fix: Re-validate Resource Binding at MCP OAuth Token Exchange

Adds a defense-in-depth re-assertion of the RFC 9728 §3.3 resource/server
binding inside `completeOAuthFlow`. Flows have a 10-minute TTL, so a flow
initiated under pre-fix (vulnerable) code could still be in-flight at upgrade
time carrying unvalidated resource metadata. Re-checking here closes that
window without requiring operators to flush flow state on deploy.

Also guards against future regressions that might re-introduce unvalidated
paths into the flow-metadata pipeline (GHSA-gvpj-vm2f-2m23).

*  test: Address Review Findings on MCP OAuth Resource Validation

Follow-up to GHSA-gvpj-vm2f-2m23 fix. Resolves three reviewer findings:

- Assert `failFlow` is called when `completeOAuthFlow` rejects at
  re-validation — locks in the "no stuck PENDING entry" guarantee that the
  catch block already provides in production code.
- Update the "no resource metadata" warning in `initiateOAuthFlow`: post-fix,
  that branch is only reachable when PRM discovery returned nothing (404,
  network error, server without RFC 9728). A document with a missing
  `resource` field now throws earlier in `assertResourceBoundToServer`, so
  the old "missing 'resource' property" phrasing described a case that can
  no longer reach this branch.
- Add a test for an unparseable `resource` string triggering the
  error-wrapping path in `assertResourceBoundToServer` (verifies the wrapper
  surfaces a descriptive message instead of leaking a raw `TypeError:
  Invalid URL` from the SDK's `new URL()` call).
2026-04-21 05:44:09 -07:00
..
src 🔒 fix: Validate MCP OAuth Protected Resource Metadata binding (#12755) 2026-04-21 05:44:09 -07:00
types 🔬 ci: Add TypeScript Type Checks to Backend Workflow and Fix All Type Errors (#12451) 2026-03-28 21:06:39 -04:00
.gitignore 🧠 feat: User Memories for Conversational Context (#7760) 2025-06-07 18:52:22 -04:00
babel.config.cjs 🧠 feat: User Memories for Conversational Context (#7760) 2025-06-07 18:52:22 -04:00
jest.config.mjs refactor: Use in-memory cache for App MCP configs to avoid Redis SCAN (#12410) 2026-03-26 14:44:31 -04:00
package.json 📦 chore: Update @librechat/agentsto v3.1.68 (#12752) 2026-04-20 17:45:58 -07:00
rollup.config.js 🔄 refactor: Migrate Cache Logic to TypeScript (#9771) 2025-10-02 09:33:58 -04:00
tsconfig-paths-bootstrap.mjs 🧠 feat: User Memories for Conversational Context (#7760) 2025-06-07 18:52:22 -04:00
tsconfig.build.json 🧑‍💻 refactor: Secure Field Selection for 2FA & API Build Sourcemap (#9087) 2025-08-15 18:55:49 -04:00
tsconfig.json 🌊 feat: Resumable LLM Streams with Horizontal Scaling (#10926) 2025-12-19 12:14:19 -05:00
tsconfig.spec.json 🧠 feat: User Memories for Conversational Context (#7760) 2025-06-07 18:52:22 -04:00