diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index aaa172ea66..41c76b1f2b 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -487,6 +487,33 @@ describe('Auth Adapter features', () => { expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(2); }); + it('should not perform authData validation twice when data mutated', async () => { + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); + await reconfigureServer({ + auth: { baseAdapter }, + allowExpiredAuthDataToken: false, + }); + + const user = new Parse.User(); + + await user.save({ + authData: { + baseAdapter: { id: 'baseAdapter', token: "sometoken1" }, + }, + }); + + expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1); + + const user2 = new Parse.User(); + await user2.save({ + authData: { + baseAdapter: { id: 'baseAdapter', token: "sometoken2" }, + }, + }); + + expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); + }); + it('should require additional provider if configured', async () => { await reconfigureServer({ auth: { baseAdapter, additionalAdapter }, @@ -937,7 +964,7 @@ describe('Auth Adapter features', () => { allowExpiredAuthDataToken: false, }); logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + spyOn(logger, 'error').and.callFake(() => { }); user = new Parse.User(); await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); const user2 = new Parse.User(); diff --git a/src/RestWrite.js b/src/RestWrite.js index b93bb2d0c8..c2d4e1580c 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -523,10 +523,14 @@ RestWrite.prototype.handleAuthData = async function (authData) { const r = await Auth.findUsersWithAuthData(this.config, authData); const results = this.filteredObjectsByACL(r); - if (results.length > 1) { + const userId = this.getUserId(); + const userResult = results[0]; + const foundUserIsNotCurrentUser = userId && userResult && userId !== userResult.objectId; + + if (results.length > 1 || foundUserIsNotCurrentUser) { // To avoid https://github.com/parse-community/parse-server/security/advisories/GHSA-8w3j-g983-8jh5 // Let's run some validation before throwing - await Auth.handleAuthDataValidation(authData, this, results[0]); + await Auth.handleAuthDataValidation(authData, this, userResult); throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } @@ -544,13 +548,6 @@ RestWrite.prototype.handleAuthData = async function (authData) { // User found with provided authData if (results.length === 1) { - const userId = this.getUserId(); - const userResult = results[0]; - // Prevent duplicate authData id - if (userId && userId !== userResult.objectId) { - await Auth.handleAuthDataValidation(authData, this, results[0]); - throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); - } this.storage.authProvider = Object.keys(authData).join(',');