LibreChat/api/server
Danny Avila fa4a43da21
🔐 fix: Strip code_challenge from Admin OAuth requests before Passport (#12534)
* 🔐 fix: Strip code_challenge from admin OAuth requests before Passport

openid-client v6's Passport Strategy uses `currentUrl.searchParams.size === 0`
to distinguish initial authorization requests from OAuth callbacks. The
admin-panel-specific `code_challenge` query parameter caused the strategy to
misclassify the request as a callback and return 401 Unauthorized.

* 🔐 fix: Strip code_challenge from admin OAuth requests before Passport

openid-client v6's Passport Strategy uses `currentUrl.searchParams.size === 0`
to distinguish initial authorization requests from OAuth callbacks. The
admin-panel-specific `code_challenge` query parameter caused the strategy to
misclassify the request as a callback and return 401 Unauthorized.

- Fix regex to handle `code_challenge` in any query position without producing
  malformed URLs, and handle empty `code_challenge=` values (`[^&]*` vs `[^&]+`)
- Combine `storePkceChallenge` + `stripCodeChallenge` into a single
  `storeAndStripChallenge` helper to enforce read-store-strip ordering
- Apply defensively to all 7 admin OAuth providers
- Add 12 unit tests covering stripCodeChallenge and storeAndStripChallenge

* refactor: Extract PKCE helpers to utility file, harden tests

- Move stripCodeChallenge and storeAndStripChallenge to
  api/server/utils/adminPkce.js — eliminates _test production export
  and avoids loading the full auth.js module tree in tests
- Add missing req.originalUrl/req.url assertions to invalid-challenge
  and no-challenge test branches (regression blind spots)
- Hoist cache reference to module scope in tests (was redundantly
  re-acquired from mock factory on every beforeEach)

* chore: Address review NITs — imports, exports, naming, assertions

- Fix import order in auth.js (longest-to-shortest per CLAUDE.md)
- Remove unused PKCE_CHALLENGE_TTL/PKCE_CHALLENGE_PATTERN exports
- Hoist strip arrow to module-scope stripChallengeFromUrl
- Rename auth.test.js → auth.spec.js (project convention)
- Tighten cache-failure test: toBe instead of toContain, add req.url

* refactor: Move PKCE helpers to packages/api with dependency injection

Move stripCodeChallenge and storeAndStripChallenge from api/server/utils
into packages/api/src/auth/exchange.ts alongside the existing PKCE
verification logic. Cache is now injected as a Keyv parameter, matching
the dependency-injection pattern used throughout packages/api/.

- Add PkceStrippableRequest interface for minimal req typing
- auth.js imports storeAndStripChallenge from @librechat/api
- Delete api/server/utils/adminPkce.js
- Move tests to packages/api/src/auth/adminPkce.spec.ts (TypeScript,
  real Keyv instances, no getLogStores mock needed)
2026-04-02 21:03:44 -04:00
..
controllers 🔁 fix: Pass recursionLimit to OpenAI-Compatible Agents API Endpoint (#12510) 2026-04-01 21:13:07 -04:00
middleware 🏗️ refactor: Remove Redundant Caching, Migrate Config Services to TypeScript (#12466) 2026-03-30 16:49:48 -04:00
routes 🔐 fix: Strip code_challenge from Admin OAuth requests before Passport (#12534) 2026-04-02 21:03:44 -04:00
services 🎯 fix: MCP Tool Misclassification from Action Delimiter Collision (#12512) 2026-04-01 22:36:21 -04:00
utils 🏗️ refactor: Remove Redundant Caching, Migrate Config Services to TypeScript (#12466) 2026-03-30 16:49:48 -04:00
cleanup.js 🧹 refactor: Tighten Config Schema Typing and Remove Deprecated Fields (#12452) 2026-03-29 01:10:57 -04:00
experimental.js ⚖️ refactor: Split Config Route into Unauthenticated and Authenticated Paths (#12490) 2026-03-31 19:22:51 -04:00
index.js ⚖️ refactor: Split Config Route into Unauthenticated and Authenticated Paths (#12490) 2026-03-31 19:22:51 -04:00
index.spec.js 🚦 fix: 404 JSON Responses for Unmatched API Routes (#11976) 2026-02-27 22:49:54 -05:00
socialLogins.js 🔐 feat: Admin Auth Support for SAML and Social OAuth Providers (#12472) 2026-03-30 22:49:44 -04:00