Skip to content

Commit 48a27b2

Browse files
committed
Added AdditionalUserInfo
1 parent 48c6624 commit 48a27b2

File tree

4 files changed

+268
-0
lines changed

4 files changed

+268
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { expect } from 'chai';
19+
import { IdTokenResponse } from '../../model/id_token';
20+
import { ProviderId } from '../providers';
21+
import { fromIdTokenResponse } from './additional_user_info';
22+
23+
describe('core/user/additional_user_info', () => {
24+
25+
describe('fromIdTokenResponse', () => {
26+
const profile = {login: 'scott', friends: [], netWorth: 5.00};
27+
const rawUserInfo = JSON.stringify(profile);
28+
29+
describe('parses federated IDP response tokens', () => {
30+
31+
it('for FacebookAdditionalUserInfo', () => {
32+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK, rawUserInfo}))!;
33+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
34+
expect(isNewUser).to.be.false;
35+
expect(providerId).to.eq(ProviderId.FACEBOOK);
36+
expect(username).to.be.null;
37+
expect(profile).to.eq(profile);
38+
});
39+
40+
it('for GithubAdditionalUserInfo', () => {
41+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.GITHUB, rawUserInfo}))!;
42+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
43+
expect(isNewUser).to.be.false;
44+
expect(providerId).to.eq(ProviderId.GITHUB);
45+
expect(username).to.eq('scott');
46+
expect(profile).to.eq(profile);
47+
});
48+
49+
it('for GoogleAdditionalUserInfo', () => {
50+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.GOOGLE, rawUserInfo}))!;
51+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
52+
expect(isNewUser).to.be.false;
53+
expect(providerId).to.eq(ProviderId.GOOGLE);
54+
expect(username).to.be.null;
55+
expect(profile).to.eq(profile);
56+
});
57+
58+
it('for TwitterAdditionalUserInfo', () => {
59+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.TWITTER, rawUserInfo, screenName: 'scott'}))!;
60+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
61+
expect(isNewUser).to.be.false;
62+
expect(providerId).to.eq(ProviderId.TWITTER);
63+
expect(username).to.eq('scott');
64+
expect(profile).to.eq(profile);
65+
});
66+
});
67+
68+
describe('parses profile data', () => {
69+
70+
it('for valid JSON', () => {
71+
expect(fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK, rawUserInfo}))!.profile).to.deep.eq(profile);
72+
});
73+
74+
it('for missing JSON', () => {
75+
expect(fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK}))!.profile).to.deep.eq({});
76+
});
77+
});
78+
79+
describe('determines new-user status', () => {
80+
81+
it('for new users by token response', () => {
82+
expect(fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK, isNewUser: true}))!.isNewUser).to.be.true;
83+
});
84+
85+
it('for new users by toolkit response kind', () => {
86+
expect(fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK, kind: 'identitytoolkit#SignupNewUserResponse'}))!.isNewUser).to.be.true;
87+
});
88+
89+
it('for old users', () => {
90+
expect(fromIdTokenResponse(idTokenResponse({providerId: ProviderId.FACEBOOK}))!.isNewUser).to.be.false;
91+
});
92+
});
93+
94+
describe('creates generic AdditionalUserInfo', () => {
95+
96+
it('for custom auth', () => {
97+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.CUSTOM, rawUserInfo}))!;
98+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
99+
expect(isNewUser).to.be.false;
100+
expect(providerId).to.be.null;
101+
expect(username).to.be.null;
102+
expect(profile).to.eq(profile);
103+
});
104+
105+
it('for anonymous auth', () => {
106+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({providerId: ProviderId.ANONYMOUS, rawUserInfo}))!;
107+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
108+
expect(isNewUser).to.be.false;
109+
expect(providerId).to.be.null;
110+
expect(username).to.be.null;
111+
expect(profile).to.eq(profile);
112+
});
113+
/*
114+
it('for missing provider IDs in response but not in token', () => {
115+
const additionalUserInfo = fromIdTokenResponse(idTokenResponse({rawUserInfo}))!;
116+
const {isNewUser, providerId, username, profile} = additionalUserInfo;
117+
expect(isNewUser).to.be.false;
118+
expect(providerId).to.eq(ProviderId.FACEBOOK);
119+
expect(username).to.be.null;
120+
expect(profile).to.eq(profile);
121+
});
122+
*/
123+
});
124+
125+
describe('returns null', () => {
126+
it('for missing provider IDs', () => {
127+
expect(fromIdTokenResponse(idTokenResponse({}))).to.be.null;
128+
});
129+
});
130+
});
131+
});
132+
133+
function idTokenResponse(partial: PartialIdTokenResponse): IdTokenResponse {
134+
return {
135+
idToken: 'Parsing logic not implemented',
136+
refreshToken: 'Doesn\'t matter',
137+
expiresIn: 'Doesn\'t matter',
138+
localId: 'Doesn\'t matter',
139+
kind: 'Not a new user response ',
140+
...partial
141+
};
142+
}
143+
144+
interface PartialIdTokenResponse {
145+
providerId?: ProviderId;
146+
isNewUser?: boolean;
147+
rawUserInfo?: string;
148+
screenName?: string | null;
149+
displayName?: string | null;
150+
photoUrl?: string | null;
151+
kind?: string;
152+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import { IdTokenResponse } from '../../model/id_token';
18+
import { ProviderId } from '../providers';
19+
import { AdditionalUserInfo } from '../../model/user';
20+
21+
export function fromIdTokenResponse(
22+
idTokenResponse: IdTokenResponse
23+
): AdditionalUserInfo | null {
24+
const { providerId } = idTokenResponse;
25+
const profile =
26+
typeof idTokenResponse.rawUserInfo === 'string'
27+
? JSON.parse(idTokenResponse.rawUserInfo)
28+
: {};
29+
const isNewUser =
30+
!!idTokenResponse.isNewUser ||
31+
idTokenResponse.kind === 'identitytoolkit#SignupNewUserResponse';
32+
/*
33+
if (!providerId && !!idTokenResponse) {
34+
const providerId = parseIdToken(idTokenResponse.idToken).signInProvider;
35+
return new GenericAdditionalUserInfo(isNewUser, providerId, null, profile);
36+
}
37+
*/
38+
if (!providerId) {
39+
return null;
40+
}
41+
switch (providerId) {
42+
case ProviderId.FACEBOOK:
43+
return new FacebookAdditionalUserInfo(isNewUser, profile);
44+
case ProviderId.GITHUB:
45+
return new GithubAdditionalUserInfo(isNewUser, profile);
46+
case ProviderId.GOOGLE:
47+
return new GoogleAdditionalUserInfo(isNewUser, profile);
48+
case ProviderId.TWITTER:
49+
return new TwitterAdditionalUserInfo(
50+
isNewUser,
51+
profile,
52+
idTokenResponse.screenName || null
53+
);
54+
case ProviderId.CUSTOM:
55+
case ProviderId.ANONYMOUS:
56+
return new GenericAdditionalUserInfo(isNewUser, null, null, profile);
57+
default:
58+
return new GenericAdditionalUserInfo(
59+
isNewUser,
60+
providerId,
61+
null,
62+
profile
63+
);
64+
}
65+
}
66+
67+
class GenericAdditionalUserInfo implements AdditionalUserInfo {
68+
constructor(
69+
readonly isNewUser: boolean,
70+
readonly providerId: ProviderId | null,
71+
readonly username: string | null,
72+
readonly profile: { [key: string]: unknown } | null
73+
) {}
74+
}
75+
76+
class FacebookAdditionalUserInfo extends GenericAdditionalUserInfo {
77+
constructor(isNewUser: boolean, profile: { [key: string]: unknown } | null) {
78+
super(isNewUser, ProviderId.FACEBOOK, null, profile);
79+
}
80+
}
81+
82+
class GithubAdditionalUserInfo extends GenericAdditionalUserInfo {
83+
constructor(isNewUser: boolean, profile: { [key: string]: unknown } | null) {
84+
super(
85+
isNewUser,
86+
ProviderId.GITHUB,
87+
typeof profile?.login === 'string' ? profile?.login : null,
88+
profile
89+
);
90+
}
91+
}
92+
93+
class GoogleAdditionalUserInfo extends GenericAdditionalUserInfo {
94+
constructor(isNewUser: boolean, profile: { [key: string]: unknown } | null) {
95+
super(isNewUser, ProviderId.GOOGLE, null, profile);
96+
}
97+
}
98+
99+
class TwitterAdditionalUserInfo extends GenericAdditionalUserInfo {
100+
constructor(
101+
isNewUser: boolean,
102+
profile: { [key: string]: unknown } | null,
103+
screenName: string | null
104+
) {
105+
super(isNewUser, ProviderId.TWITTER, screenName, profile);
106+
}
107+
}

packages-exp/auth-exp/src/model/id_token.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ export interface IdTokenResponse {
5555
// MFA-specific fields
5656
mfaPendingCredential?: string;
5757
mfaInfo?: APIMFAInfo[];
58+
59+
// Used in AdditionalUserInfo
5860
isNewUser?: boolean;
5961
rawUserInfo?: string;
6062
screenName?: string | null;

packages-exp/auth-exp/src/model/user.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ import { ProviderId } from '../core/providers';
2020
import { Auth } from './auth';
2121
import { IdTokenResult } from './id_token';
2222

23+
export interface AdditionalUserInfo {
24+
readonly isNewUser: boolean;
25+
readonly profile: { [key: string]: unknown } | null;
26+
readonly providerId: ProviderId | null;
27+
readonly username: string | null;
28+
}
29+
2330
export interface UserMetadata {
2431
readonly creationTime?: string;
2532
readonly lastSignInTime?: string;

0 commit comments

Comments
 (0)