Skip to content

Commit 3f4cbf7

Browse files
authored
Fix anonymous auth relogin edge case with additionalUserInfo (#3961)
* Handle anonymous auth re-login edge case * Formatting
1 parent 64c8d04 commit 3f4cbf7

File tree

2 files changed

+97
-14
lines changed

2 files changed

+97
-14
lines changed

packages-exp/auth-exp/src/core/user/additional_user_info.test.ts

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,33 @@
1717

1818
import { expect } from 'chai';
1919

20-
import { ProviderId, UserProfile } from '@firebase/auth-types-exp';
20+
import {
21+
OperationType,
22+
ProviderId,
23+
UserProfile
24+
} from '@firebase/auth-types-exp';
2125

2226
import { IdTokenResponse, IdTokenResponseKind } from '../../model/id_token';
23-
import { _fromIdTokenResponse } from './additional_user_info';
27+
import {
28+
_fromIdTokenResponse,
29+
getAdditionalUserInfo
30+
} from './additional_user_info';
2431
import { base64Encode } from '@firebase/util';
32+
import { UserCredentialImpl } from './user_credential_impl';
33+
import { Auth } from '../../model/auth';
34+
import { User, UserCredential } from '../../model/user';
35+
import { testAuth, testUser } from '../../../test/helpers/mock_auth';
2536

2637
describe('core/user/additional_user_info', () => {
38+
const userProfileWithLogin: UserProfile = {
39+
login: 'scott',
40+
friends: [],
41+
netWorth: 5.0
42+
};
43+
const rawUserInfoWithLogin = JSON.stringify(userProfileWithLogin);
44+
const userProfileNoLogin: UserProfile = { sample: 'data' };
45+
const rawUserInfoNoLogin = JSON.stringify(userProfileNoLogin);
2746
describe('_fromIdTokenResponse', () => {
28-
const userProfileWithLogin: UserProfile = {
29-
login: 'scott',
30-
friends: [],
31-
netWorth: 5.0
32-
};
33-
const rawUserInfoWithLogin = JSON.stringify(userProfileWithLogin);
34-
const userProfileNoLogin: UserProfile = { sample: 'data' };
35-
const rawUserInfoNoLogin = JSON.stringify(userProfileNoLogin);
36-
3747
describe('parses federated IDP response tokens', () => {
3848
it('for FacebookAdditionalUserInfo', () => {
3949
const idResponse = idTokenResponse({
@@ -211,6 +221,70 @@ describe('core/user/additional_user_info', () => {
211221
});
212222
});
213223
});
224+
225+
describe('getAdditionalUserInfo()', () => {
226+
let auth: Auth;
227+
let user: User;
228+
let cred: UserCredential;
229+
beforeEach(async () => {
230+
auth = await testAuth();
231+
user = testUser(auth, 'uid');
232+
cred = new UserCredentialImpl({
233+
user,
234+
providerId: null,
235+
operationType: OperationType.SIGN_IN
236+
});
237+
});
238+
239+
it('calls through to _fromIdTokenResponse', () => {
240+
cred._tokenResponse = idTokenResponse({
241+
providerId: ProviderId.ANONYMOUS,
242+
rawUserInfo: rawUserInfoWithLogin
243+
});
244+
const {
245+
isNewUser,
246+
providerId,
247+
username,
248+
profile
249+
} = getAdditionalUserInfo(cred)!;
250+
expect(isNewUser).to.be.false;
251+
expect(providerId).to.be.null;
252+
expect(username).to.be.undefined;
253+
expect(profile).to.eq(profile);
254+
});
255+
256+
it('calls through to _fromIdTokenResponse preserving isNewUser', () => {
257+
cred._tokenResponse = idTokenResponse({
258+
providerId: ProviderId.ANONYMOUS,
259+
rawUserInfo: rawUserInfoWithLogin,
260+
isNewUser: true
261+
});
262+
const {
263+
isNewUser,
264+
providerId,
265+
username,
266+
profile
267+
} = getAdditionalUserInfo(cred)!;
268+
expect(isNewUser).to.be.true;
269+
expect(providerId).to.be.null;
270+
expect(username).to.be.undefined;
271+
expect(profile).to.eq(profile);
272+
});
273+
274+
it('returns bespoke info if existing anonymous user', () => {
275+
// Note that _tokenResponse is not set on cred
276+
((user as unknown) as Record<string, unknown>).isAnonymous = true;
277+
const { isNewUser, providerId, profile } = getAdditionalUserInfo(cred)!;
278+
expect(isNewUser).to.be.false;
279+
expect(providerId).to.be.null;
280+
expect(profile).to.eq(profile);
281+
});
282+
283+
it('returns null if not anonymous', () => {
284+
// Note that _tokenResponse is not set on cred
285+
expect(getAdditionalUserInfo(cred)).to.be.null;
286+
});
287+
});
214288
});
215289

216290
function idTokenResponse(partial: Partial<IdTokenResponse>): IdTokenResponse {

packages-exp/auth-exp/src/core/user/additional_user_info.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,16 @@ class TwitterAdditionalUserInfo extends FederatedAdditionalUserInfoWithUsername
129129
export function getAdditionalUserInfo(
130130
userCredential: externs.UserCredential
131131
): externs.AdditionalUserInfo | null {
132-
return _fromIdTokenResponse(
133-
(userCredential as UserCredential)._tokenResponse
134-
);
132+
const { user, _tokenResponse } = userCredential as UserCredential;
133+
if (user.isAnonymous && !_tokenResponse) {
134+
// Handle the special case where signInAnonymously() gets called twice.
135+
// No network call is made so there's nothing to actually fill this in
136+
return {
137+
providerId: null,
138+
isNewUser: false,
139+
profile: null
140+
};
141+
}
142+
143+
return _fromIdTokenResponse(_tokenResponse);
135144
}

0 commit comments

Comments
 (0)