* 🔐 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)