Parse Server subject to Improper Authentication allowing Auth adapter app ID validation to be circumvented
Description
Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. In versions prior to 4.10.16, or from 5.0.0 to 5.2.6, validation of the authentication adapter app ID for _Facebook_ and _Spotify_ may be circumvented. Configurations which allow users to authenticate using the Parse Server authentication adapter where appIds is set as a string instead of an array of strings authenticate requests from an app with a different app ID than the one specified in the appIds configuration. For this vulnerability to be exploited, an attacker needs to be assigned an app ID by the authentication provider which is a sub-set of the server-side configured app ID. This issue is patched in versions 4.10.16 and 5.2.7. There are no known workarounds.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
parse-servernpm | < 4.10.16 | 4.10.16 |
parse-servernpm | >= 5.0.0, < 5.2.7 | 5.2.7 |
Affected products
1- Range: < 4.10.16
Patches
18c8ec715739efix: authentication adapter app ID validation may be circumvented; this fixes a vulnerability that affects configurations which allow users to authenticate using the Parse Server authentication adapter for *Facebook* or *Spotify* and where the server-side authentication adapter configuration `appIds` is set as a string (e.g. `abc`) instead of an array of strings (e.g. `["abc"]`) ([GHSA-r657-33vp-gp22](https://github.com/parse-community/parse-server/security/advisories/GHSA-r657-33vp-gp22)) [skip release] (#8187)
3 files changed · +41 −16
spec/AuthenticationAdapters.spec.js+23 −0 modified@@ -419,6 +419,29 @@ describe('AuthenticationProviders', function () { expect(providerOptions).toEqual(options.facebook); }); + it('should throw error when Facebook request appId is wrong data type', async () => { + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); + spyOn(httpsRequest, 'get').and.callFake(() => { + return Promise.resolve({ id: 'a' }); + }); + const options = { + facebook: { + appIds: 'abcd', + appSecret: 'secret_sauce', + }, + }; + const authData = { + access_token: 'badtoken', + }; + const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( + 'facebook', + options + ); + await expectAsync(adapter.validateAppId(appIds, authData, providerOptions)).toBeRejectedWith( + new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.') + ); + }); + it('should handle Facebook appSecret for validating appIds', async () => { const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); spyOn(httpsRequest, 'get').and.callFake(() => {
src/Adapters/Auth/facebook.js+10 −9 modified@@ -32,22 +32,23 @@ function validateGraphToken(authData, options) { }); } -function validateGraphAppId(appIds, authData, options) { +async function validateGraphAppId(appIds, authData, options) { var access_token = authData.access_token; if (process.env.TESTING && access_token === 'test') { - return Promise.resolve(); + return; + } + if (!Array.isArray(appIds)) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.'); } if (!appIds.length) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.'); } - return graphRequest( - 'app?access_token=' + access_token + getAppSecretPath(authData, options) - ).then(data => { - if (data && appIds.indexOf(data.id) != -1) { - return; - } + const data = await graphRequest( + `app?access_token=${access_token}${getAppSecretPath(authData, options)}` + ); + if (!data || !appIds.includes(data.id)) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.'); - }); + } } const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
src/Adapters/Auth/spotify.js+8 −7 modified@@ -13,17 +13,18 @@ function validateAuthData(authData) { } // Returns a promise that fulfills if this app id is valid. -function validateAppId(appIds, authData) { - var access_token = authData.access_token; +async function validateAppId(appIds, authData) { + const access_token = authData.access_token; + if (!Array.isArray(appIds)) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.'); + } if (!appIds.length) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is not configured.'); } - return request('me', access_token).then(data => { - if (data && appIds.indexOf(data.id) != -1) { - return; - } + const data = await request('me', access_token); + if (!data || !appIds.includes(data.id)) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.'); - }); + } } // A promisey wrapper for Spotify API requests.
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-r657-33vp-gp22ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-39231ghsaADVISORY
- github.com/parse-community/parse-server/commit/8c8ec715739e0f851338cfed794409ebac66c51bghsaWEB
- github.com/parse-community/parse-server/releases/tag/4.10.16ghsaWEB
- github.com/parse-community/parse-server/releases/tag/5.2.7ghsaWEB
- github.com/parse-community/parse-server/security/advisories/GHSA-r657-33vp-gp22ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.