From 55528a60591694089f80d3f9cdafced4c330243d Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 25 Jul 2017 13:39:36 -0400 Subject: [PATCH 1/4] Fixes issue affecting linking users to a 3rd party auth --- spec/AuthenticationAdapters.spec.js | 39 +++++++++++++++++++++++++---- src/RestWrite.js | 12 ++++++--- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index 37763fcc27..3294d6ecea 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -71,6 +71,10 @@ describe('AuthenticationProviers', function() { }); var createOAuthUser = function(callback) { + return createOAuthUserWithSessionToken(undefined, callback); + } + + var createOAuthUserWithSessionToken = function(token, callback) { var jsonBody = { authData: { myoauth: getMockMyOauthProvider().authData @@ -81,18 +85,27 @@ describe('AuthenticationProviers', function() { headers: {'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest', 'X-Parse-Installation-Id': 'yolo', + 'X-Parse-Session-Token': token, 'Content-Type': 'application/json' }, url: 'http://localhost:8378/1/users', - body: JSON.stringify(jsonBody) + body: jsonBody, + json: true }; - return request.post(options, callback); + return new Promise((resolve) => { + request.post(options, (err, res, body) => { + resolve({err, res, body}); + if (callback) { + callback(err, res, body); + } + }); + }); } it("should create user with REST API", done => { createOAuthUser((error, response, body) => { expect(error).toBe(null); - var b = JSON.parse(body); + var b = body; ok(b.sessionToken); expect(b.objectId).not.toBeNull(); expect(b.objectId).not.toBeUndefined(); @@ -118,14 +131,14 @@ describe('AuthenticationProviers', function() { var objectId; createOAuthUser((error, response, body) => { expect(error).toBe(null); - var b = JSON.parse(body); + var b = body expect(b.objectId).not.toBeNull(); expect(b.objectId).not.toBeUndefined(); objectId = b.objectId; createOAuthUser((error, response, body) => { expect(error).toBe(null); - var b = JSON.parse(body); + var b = body; expect(b.objectId).not.toBeNull(); expect(b.objectId).not.toBeUndefined(); expect(b.objectId).toBe(objectId); @@ -134,6 +147,22 @@ describe('AuthenticationProviers', function() { }); }); + it("should fail to link if session token don't match user", (done) => { + Parse.User.signUp('myUser', 'password').then((user) => { + return createOAuthUserWithSessionToken(user.getSessionToken()); + }).then(() => { + return Parse.User.logOut(); + }).then(() => { + return Parse.User.signUp('myUser2', 'password'); + }).then((user) => { + return createOAuthUserWithSessionToken(user.getSessionToken()); + }).then(({ body }) => { + expect(body.code).toBe(208); + expect(body.error).toBe('this auth is already used'); + done(); + }).catch(done.fail); + }); + it("unlink and link with custom provider", (done) => { var provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); diff --git a/src/RestWrite.js b/src/RestWrite.js index fffc7f9135..16c34982d1 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -297,7 +297,13 @@ RestWrite.prototype.handleAuthData = function(authData) { } }); const hasMutatedAuthData = Object.keys(mutatedAuthData).length !== 0; - if (!this.query) { + let userId; + if (this.query && this.query.objectId) { + userId = this.query.objectId; + } else if (this.auth && this.auth.user && this.auth.user.id) { + userId = this.auth.user.id; + } + if (!userId) { // no user making the call // Login with auth data delete results[0].password; @@ -328,10 +334,10 @@ RestWrite.prototype.handleAuthData = function(authData) { // Just update the authData part return this.config.database.update(this.className, {objectId: this.data.objectId}, {authData: mutatedAuthData}, {}); }); - } else if (this.query && this.query.objectId) { + } else if (userId) { // Trying to update auth data but users // are different - if (userResult.objectId !== this.query.objectId) { + if (userResult.objectId !== userId) { throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } From 10155a1e76db2d42f13d5cd8fc2a62666f6fe857 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 25 Jul 2017 14:24:13 -0400 Subject: [PATCH 2/4] Fixes problematic test --- spec/ParseUser.spec.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 32c505e031..5d7018acb7 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -1178,17 +1178,19 @@ describe('Parse.User testing', () => { return user._linkWith('facebook', {}); }).then(user => { expect(user._isLinked("facebook")).toBeTruthy(); - return Parse.User._logInWith('facebook', {}); + // We should logout here as session token is passed + // Probably need a fix in the JS SDK to handle those + // linking errors + return Parse.User.logOut().then(() => { + return Parse.User._logInWith('facebook', {}); + }); }).then(user => { const fileAgain = user.get('file'); expect(fileAgain.name()).toMatch(/yolo.txt$/); expect(fileAgain.url()).toMatch(/yolo.txt$/); }).then(() => { done(); - }, error => { - jfail(error); - done(); - }); + }).catch(done.fail); }); it("log in with provider twice", (done) => { From 85dd1ff7b50f58c4455f53799d6a0748b933597c Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 25 Jul 2017 14:59:42 -0400 Subject: [PATCH 3/4] Better fix --- spec/ParseUser.spec.js | 4 +--- src/RestWrite.js | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 5d7018acb7..227f94c4fe 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -1181,9 +1181,7 @@ describe('Parse.User testing', () => { // We should logout here as session token is passed // Probably need a fix in the JS SDK to handle those // linking errors - return Parse.User.logOut().then(() => { - return Parse.User._logInWith('facebook', {}); - }); + return Parse.User._logInWith('facebook', {}); }).then(user => { const fileAgain = user.get('file'); expect(fileAgain.name()).toMatch(/yolo.txt$/); diff --git a/src/RestWrite.js b/src/RestWrite.js index 16c34982d1..199cd62f77 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -303,7 +303,8 @@ RestWrite.prototype.handleAuthData = function(authData) { } else if (this.auth && this.auth.user && this.auth.user.id) { userId = this.auth.user.id; } - if (!userId) { // no user making the call + if (!userId || userId === userResult.objectId) { // no user making the call + // OR the user making the call is the right one // Login with auth data delete results[0].password; From ef6274688042cfb08ed8314761a4d3b7716f8467 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Tue, 25 Jul 2017 15:00:10 -0400 Subject: [PATCH 4/4] nits --- spec/ParseUser.spec.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 227f94c4fe..69f3de6230 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -1178,9 +1178,6 @@ describe('Parse.User testing', () => { return user._linkWith('facebook', {}); }).then(user => { expect(user._isLinked("facebook")).toBeTruthy(); - // We should logout here as session token is passed - // Probably need a fix in the JS SDK to handle those - // linking errors return Parse.User._logInWith('facebook', {}); }).then(user => { const fileAgain = user.get('file');