🔏 fix: Remove Federated Tokens from OpenID Refresh Response#12264
Merged
Conversation
The refresh controller was attaching federatedTokens (including the refresh_token) to the user object returned in the JSON response, exposing HttpOnly-protected tokens to client-side JavaScript. The tokens are already stored server-side by setOpenIDAuthTokens and re-attached by the JWT strategy on authenticated requests.
Contributor
There was a problem hiding this comment.
Pull request overview
Removes OpenID federated token data (including refresh tokens) from the /refresh endpoint’s JSON response to prevent exposure of HttpOnly-protected credentials to client-side JavaScript.
Changes:
- Stop attaching
user.federatedTokensin the OpenID refresh controller response. - Add a Jest test asserting
federatedTokensis not included in the refresh response user payload.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| api/server/controllers/AuthController.js | Removes user.federatedTokens construction before sending the refresh response. |
| api/server/controllers/AuthController.spec.js | Adds a regression test to ensure refresh responses don’t include federatedTokens. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+256
to
+261
| expect(res.send).toHaveBeenCalledWith({ | ||
| token: 'new-app-token', | ||
| user: expect.not.objectContaining({ | ||
| federatedTokens: expect.anything(), | ||
| }), | ||
| }); |
The OpenID refresh path returned the raw findOpenIDUser result without field projection, unlike the non-OpenID path which excludes password, __v, totpSecret, and backupCodes via getUserById projection. Destructure out sensitive fields before serializing. Also strengthens the regression test: uses not.toHaveProperty for true property-absence checks (expect.anything() misses null/undefined), adds positive shape assertion, and DRYs up duplicated mock user setup.
jcbartle
pushed a commit
to jcbartle/LibreChat
that referenced
this pull request
May 11, 2026
…ila#12264) * 🔒 fix: Remove OpenID federated tokens from refresh endpoint response The refresh controller was attaching federatedTokens (including the refresh_token) to the user object returned in the JSON response, exposing HttpOnly-protected tokens to client-side JavaScript. The tokens are already stored server-side by setOpenIDAuthTokens and re-attached by the JWT strategy on authenticated requests. * 🔒 fix: Strip sensitive fields from OpenID refresh response user object The OpenID refresh path returned the raw findOpenIDUser result without field projection, unlike the non-OpenID path which excludes password, __v, totpSecret, and backupCodes via getUserById projection. Destructure out sensitive fields before serializing. Also strengthens the regression test: uses not.toHaveProperty for true property-absence checks (expect.anything() misses null/undefined), adds positive shape assertion, and DRYs up duplicated mock user setup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixed two credential-exposure bugs in the OpenID
refreshControllerresponse path where server-side-protected tokens and sensitive user fields were being serialized into the JSON response body.user.federatedTokensassignment beforeres.send({ token, user })in the OpenID refresh path, which was leakingaccess_token,id_token,refresh_token, andexpires_atinto the response body — tokens already stored server-side bysetOpenIDAuthTokens(express session + HttpOnly cookies) and re-attached toreq.userby the JWT strategy on every authenticated request.password,__v,totpSecret, andbackupCodesfrom thefindOpenIDUserresult before serializing, aligning the OpenID path with the non-OpenID path's explicitgetUserByIdprojection (-password -__v -totpSecret -backupCodes), which previously left 2FA secrets and password hashes exposed for migrated users.expect.not.objectContaining({ federatedTokens: expect.anything() })withnot.toHaveProperty(...)calls (which correctly catchnull/undefinedvalues), added a positiveobjectContainingshape assertion to prevent false-green passes on empty response objects, populateddefaultUserwith realistic sensitive field values, and extracted the duplicated mock fixture into a shareddefaultUserconstant withfindOpenIDUsersetup moved tobeforeEach.Change Type
Testing
Verified with the Jest suite in the
apiworkspace (cd api && npx jest AuthController). The updatedrefreshController – OpenID pathdescribe block covers the fix via theshould not expose sensitive fields or federatedTokens in refresh responsetest, which:findOpenIDUserwith adefaultUsercontaining populatedpassword,totpSecret,backupCodes, and__vfields_id,email,openidId) viaobjectContainingnot.toHaveProperty, catching both omission and explicitnull/undefinedassignmentTest Configuration:
OPENID_REUSE_TOKENS=true(simulated viaisEnabled.mockReturnValue(true))openid-client,@librechat/api, and all model dependencies are mockedChecklist