LibreChat/api/server/controllers/__tests__
Gaurav Dubey 84043432a5
🧹 fix: Graceful MCP OAuth Revoke Cleanup When Tokens Are Missing (#12825)
* fix: graceful MCP OAuth revoke cleanup when tokens are missing (#12754)

`maybeUninstallOAuthMCP` in `api/server/controllers/UserController.js`
aborts before the DB-delete and flow-state cleanup steps whenever
`MCPTokenStorage.getTokens` throws `ReauthenticationRequiredError` —
which is exactly what happens when a user clicks "Revoke" on an MCP
server whose backend is already dead and whose refresh token is gone.
The resulting error is both surfaced to the log as a red line and, more
importantly, leaks the DB token row and OAuth flow state.

Wrap the token retrieval in try/catch following the same best-effort
pattern already used for the two `revokeOAuthToken` calls. On
`ReauthenticationRequiredError`, skip revocation silently (info log)
and continue to the cleanup steps. On any other unexpected error, log
a warning and continue — cleanup must always run.

Exported `maybeUninstallOAuthMCP` for direct unit testing and added
`api/server/controllers/__tests__/maybeUninstallOAuthMCP.spec.js` with
8 cases: early-return guards (non-MCP key, non-OAuth server, missing
client info), happy path (both tokens revoked + cleanup), both
failure-to-retrieve paths (ReauthenticationRequiredError and arbitrary
error — cleanup still runs in both), single-token path, and
revocation-call failures (cleanup still runs).

Fixes #12754.

* test: use instanceof check against real ReauthenticationRequiredError

Follow-up to the previous commit on this branch. Two changes:

1. `UserController.maybeUninstallOAuthMCP` now checks
   `error instanceof ReauthenticationRequiredError` using the real
   class imported from `@librechat/api`, instead of comparing
   `error?.name === 'ReauthenticationRequiredError'`. The name-string
   check matched any unrelated error that happened to have the same
   `.name`; the `instanceof` check is a proper identity test.

2. The accompanying spec's jest mock for `@librechat/api` now
   exposes a `ReauthenticationRequiredError` class, and the test
   imports it from that mock so the `instanceof` comparison in the
   production code holds during the test. Without this, the two
   "skips revocation ... still runs cleanup" tests threw
   `TypeError: Right-hand side of 'instanceof' is not an object`
   because the mock left the class undefined.

All 8 tests in the spec pass.
2026-04-29 09:11:12 +09:00
..
deleteUser.spec.js 🧬 feat: Scaffold Skills CRUD with ACL Sharing and File Schema (#12613) 2026-04-25 04:01:59 -04:00
deleteUserMcpServers.spec.js 🪦 fix: ACL-Safe User Account Deletion for Agents, Prompts, and MCP Servers (#12314) 2026-03-19 17:46:14 -04:00
deleteUserResourceCoverage.spec.js 🧬 feat: Scaffold Skills CRUD with ACL Sharing and File Schema (#12613) 2026-04-25 04:01:59 -04:00
maybeUninstallOAuthMCP.spec.js 🧹 fix: Graceful MCP OAuth Revoke Cleanup When Tokens Are Missing (#12825) 2026-04-29 09:11:12 +09:00
PermissionsController.spec.js 🧹 chore: Move direct model usage from PermissionsController to data-schemas 2026-03-21 15:20:15 -04:00
tools.verifyToolAuth.spec.js 🧰 refactor: Unify code-execution tools (#12767) 2026-04-25 04:02:01 -04:00
TwoFactorController.spec.js