Skip to content

Commit 7e2585c

Browse files
authored
fix: ParseUser.linkWith doesn't remove anonymous auth data (parse-community#2007)
1 parent f80d833 commit 7e2585c

File tree

3 files changed

+162
-9
lines changed

3 files changed

+162
-9
lines changed

integration/test/ParseUserTest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,11 +979,11 @@ describe('Parse User', () => {
979979
await Parse.FacebookUtils.link(user);
980980

981981
expect(Parse.FacebookUtils.isLinked(user)).toBe(true);
982-
expect(Parse.AnonymousUtils.isLinked(user)).toBe(true);
982+
expect(Parse.AnonymousUtils.isLinked(user)).toBe(false);
983983
await Parse.FacebookUtils.unlink(user);
984984

985985
expect(Parse.FacebookUtils.isLinked(user)).toBe(false);
986-
expect(Parse.AnonymousUtils.isLinked(user)).toBe(true);
986+
expect(Parse.AnonymousUtils.isLinked(user)).toBe(false);
987987
});
988988

989989
it('can link with twitter', async () => {

src/ParseUser.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,15 @@ class ParseUser extends ParseObject {
110110
throw new Error('Invalid type: authData field should be an object');
111111
}
112112
authData[authType] = options.authData;
113+
const oldAnonymousData = authData.anonymous;
114+
this.stripAnonymity();
113115

114116
const controller = CoreManager.getUserController();
115-
return controller.linkWith(this, authData, saveOpts);
117+
return controller.linkWith(this, authData, saveOpts).catch((e) => {
118+
delete authData[authType];
119+
this.restoreAnonimity(oldAnonymousData);
120+
throw e;
121+
});
116122
} else {
117123
return new Promise((resolve, reject) => {
118124
provider.authenticate({
@@ -310,6 +316,21 @@ class ParseUser extends ParseObject {
310316
return !!current && current.id === this.id;
311317
}
312318

319+
stripAnonymity() {
320+
const authData = this.get('authData');
321+
if (authData && typeof authData === 'object' && authData.hasOwnProperty('anonymous')) {
322+
// We need to set anonymous to null instead of deleting it in order to remove it from Parse.
323+
authData.anonymous = null;
324+
}
325+
}
326+
327+
restoreAnonimity(anonymousData: any) {
328+
if (anonymousData) {
329+
const authData = this.get('authData');
330+
authData.anonymous = anonymousData;
331+
}
332+
}
333+
313334
/**
314335
* Returns get("username").
315336
*
@@ -329,12 +350,7 @@ class ParseUser extends ParseObject {
329350
* @param {string} username
330351
*/
331352
setUsername(username: string) {
332-
// Strip anonymity
333-
const authData = this.get('authData');
334-
if (authData && typeof authData === 'object' && authData.hasOwnProperty('anonymous')) {
335-
// We need to set anonymous to null instead of deleting it in order to remove it from Parse.
336-
authData.anonymous = null;
337-
}
353+
this.stripAnonymity();
338354
this.set('username', username);
339355
}
340356

src/__tests__/ParseUser-test.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,143 @@ describe('ParseUser', () => {
11871187
spy.mockRestore();
11881188
});
11891189

1190+
it('can strip anonymous user on linkWith', async () => {
1191+
ParseUser.enableUnsafeCurrentUser();
1192+
ParseUser._clearCache();
1193+
CoreManager.setRESTController({
1194+
request() {
1195+
return Promise.resolve(
1196+
{
1197+
objectId: 'uidstrip',
1198+
sessionToken: 'r:123abc',
1199+
authData: {
1200+
anonymous: {
1201+
id: 'anonymousId',
1202+
},
1203+
},
1204+
},
1205+
200
1206+
);
1207+
},
1208+
ajax() {},
1209+
});
1210+
const user = await AnonymousUtils.logIn();
1211+
1212+
expect(user.get('authData').anonymous).toBeDefined();
1213+
1214+
ParseUser._setCurrentUserCache(user);
1215+
1216+
CoreManager.setRESTController({
1217+
request() {
1218+
return Promise.resolve(
1219+
{
1220+
objectId: 'uidstrip',
1221+
sessionToken: 'r:123abc',
1222+
authData: {
1223+
test: {
1224+
id: 'id',
1225+
access_token: 'access_token',
1226+
},
1227+
},
1228+
},
1229+
200
1230+
);
1231+
},
1232+
ajax() {},
1233+
});
1234+
const provider = {
1235+
authenticate(options) {
1236+
if (options.success) {
1237+
options.success(this, {
1238+
id: 'id',
1239+
access_token: 'access_token',
1240+
});
1241+
}
1242+
},
1243+
restoreAuthentication() {},
1244+
getAuthType() {
1245+
return 'test';
1246+
},
1247+
deauthenticate() {},
1248+
};
1249+
1250+
await user.linkWith(provider, null, { useMasterKey: true });
1251+
1252+
expect(user.get('authData')).toEqual({
1253+
test: { id: 'id', access_token: 'access_token' },
1254+
});
1255+
});
1256+
1257+
it('can restore anonymous user on linkWith failure', async () => {
1258+
ParseUser.enableUnsafeCurrentUser();
1259+
ParseUser._clearCache();
1260+
CoreManager.setRESTController({
1261+
request() {
1262+
return Promise.resolve(
1263+
{
1264+
objectId: 'uidrestore',
1265+
sessionToken: 'r:123abc',
1266+
authData: {
1267+
anonymous: {
1268+
id: 'anonymousId',
1269+
},
1270+
},
1271+
},
1272+
200
1273+
);
1274+
},
1275+
ajax() {},
1276+
});
1277+
const user = await AnonymousUtils.logIn();
1278+
expect(user.get('authData').anonymous).toBeDefined();
1279+
1280+
ParseUser._setCurrentUserCache(user);
1281+
1282+
const provider = {
1283+
authenticate(options) {
1284+
if (options.success) {
1285+
options.success(this, {
1286+
id: 'id',
1287+
access_token: 'access_token',
1288+
});
1289+
}
1290+
},
1291+
restoreAuthentication() {},
1292+
getAuthType() {
1293+
return 'test';
1294+
},
1295+
deauthenticate() {},
1296+
};
1297+
1298+
const UserController = CoreManager.getUserController();
1299+
CoreManager.setUserController({
1300+
linkWith(user) {
1301+
expect(user.get('authData').anonymous).toEqual(null);
1302+
return Promise.reject('authentication error');
1303+
},
1304+
currentUserAsync() {},
1305+
setCurrentUser() {},
1306+
currentUser() {},
1307+
signUp() {},
1308+
logIn() {},
1309+
become() {},
1310+
logOut() {},
1311+
me() {},
1312+
requestPasswordReset() {},
1313+
upgradeToRevocableSession() {},
1314+
requestEmailVerification() {},
1315+
verifyPassword() {},
1316+
});
1317+
try {
1318+
await user.linkWith(provider, null, { useMasterKey: true });
1319+
expect(true).toBe(false);
1320+
} catch (e) {
1321+
expect(e).toBe('authentication error');
1322+
}
1323+
expect(user.get('authData')).toEqual({ anonymous: { id: 'anonymousId' } });
1324+
CoreManager.setUserController(UserController);
1325+
});
1326+
11901327
it('can logout anonymous user', async () => {
11911328
ParseUser.enableUnsafeCurrentUser();
11921329
ParseUser._clearCache();

0 commit comments

Comments
 (0)