Skip to content

Commit 6b7b89f

Browse files
sam-gcyuchenshi
andauthored
[Auth] Add some headless IdP tests to round out our IdP WebDriver tests (#4609)
* Add headless idp tests * Formatting * Update packages-exp/auth-exp/test/integration/flows/idp.local.test.ts Co-authored-by: Yuchen Shi <[email protected]> * Update packages-exp/auth-exp/test/integration/flows/idp.local.test.ts Co-authored-by: Yuchen Shi <[email protected]> * PR feedback * Formatting * Fix linter Co-authored-by: Yuchen Shi <[email protected]>
1 parent 10bd519 commit 6b7b89f

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
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 {
19+
Auth,
20+
createUserWithEmailAndPassword,
21+
FacebookAuthProvider,
22+
getAdditionalUserInfo,
23+
GithubAuthProvider,
24+
GoogleAuthProvider,
25+
linkWithCredential,
26+
OperationType,
27+
ProviderId,
28+
signInWithCredential,
29+
signInWithEmailAndPassword,
30+
unlink,
31+
updateEmail,
32+
updatePassword,
33+
updateProfile
34+
// eslint-disable-next-line import/no-extraneous-dependencies
35+
} from '@firebase/auth-exp';
36+
import { FirebaseError } from '@firebase/util';
37+
import { expect, use } from 'chai';
38+
import * as chaiAsPromised from 'chai-as-promised';
39+
import {
40+
cleanUpTestInstance,
41+
getTestInstance,
42+
randomEmail
43+
} from '../../helpers/integration/helpers';
44+
45+
use(chaiAsPromised);
46+
47+
// These tests handle OAuth sign in, but they're totally headless (they don't
48+
// use the popup/redirect flows). For testing of the popup/redirect flows, look
49+
// under the test/integration/webdriver directory.
50+
51+
describe('Integration test: headless IdP', () => {
52+
let auth: Auth;
53+
let oauthIdToken: string;
54+
let email: string;
55+
56+
beforeEach(() => {
57+
auth = getTestInstance(/* requireEmulator */ true);
58+
email = randomEmail();
59+
oauthIdToken = JSON.stringify({
60+
email,
61+
'email_verified': true,
62+
sub: `oauthidp--${email}--oauthidp`
63+
});
64+
});
65+
66+
afterEach(async () => {
67+
await cleanUpTestInstance(auth);
68+
});
69+
70+
it('signs in with an OAuth token', async () => {
71+
const cred = await signInWithCredential(
72+
auth,
73+
GoogleAuthProvider.credential(oauthIdToken)
74+
);
75+
expect(auth.currentUser).to.eq(cred.user);
76+
expect(cred.operationType).to.eq(OperationType.SIGN_IN);
77+
78+
// Make sure the user is setup correctly
79+
const { user } = cred;
80+
expect(user.isAnonymous).to.be.false;
81+
expect(user.emailVerified).to.be.true;
82+
expect(user.providerData.length).to.eq(1);
83+
expect(user.providerData[0].providerId).to.eq('google.com');
84+
expect(user.providerData[0].email).to.eq(email);
85+
86+
// Make sure the additional user info is good
87+
const additionalUserInfo = getAdditionalUserInfo(cred)!;
88+
expect(additionalUserInfo.isNewUser).to.be.true;
89+
expect(additionalUserInfo.providerId).to.eq('google.com');
90+
});
91+
92+
it('allows the user to update profile', async () => {
93+
const credential = GithubAuthProvider.credential(oauthIdToken);
94+
const { user } = await signInWithCredential(auth, credential);
95+
96+
await updateProfile(user, {
97+
displayName: 'David Copperfield',
98+
photoURL: 'http://photo.test/david.png'
99+
});
100+
101+
// Check everything first
102+
expect(user.displayName).to.eq('David Copperfield');
103+
expect(user.photoURL).to.eq('http://photo.test/david.png');
104+
105+
await auth.signOut();
106+
107+
// Sign in again and double check; look at current user this time
108+
await signInWithCredential(auth, credential);
109+
expect(auth.currentUser!.displayName).to.eq('David Copperfield');
110+
expect(auth.currentUser!.photoURL).to.eq('http://photo.test/david.png');
111+
});
112+
113+
it('allows the user to change the email', async () => {
114+
const credential = FacebookAuthProvider.credential(oauthIdToken);
115+
const { user } = await signInWithCredential(auth, credential);
116+
117+
expect(user.email).to.eq(email);
118+
expect(user.emailVerified).to.be.true;
119+
120+
const newEmail = randomEmail();
121+
await updateEmail(user, newEmail);
122+
123+
// Check everything first
124+
expect(user.email).to.eq(newEmail);
125+
expect(user.emailVerified).to.be.false;
126+
127+
await auth.signOut();
128+
129+
// Sign in again
130+
await signInWithCredential(auth, credential);
131+
expect(auth.currentUser!.email).to.eq(newEmail);
132+
});
133+
134+
it('allows the user to set a password', async () => {
135+
const credential = GoogleAuthProvider.credential(oauthIdToken);
136+
const { user } = await signInWithCredential(auth, credential);
137+
138+
expect(user.providerData.length).to.eq(1);
139+
expect(user.providerData[0].providerId).to.eq('google.com');
140+
141+
// Set the password and check provider data
142+
await updatePassword(user, 'password');
143+
expect(user.providerData.length).to.eq(2);
144+
expect(user.providerData.map(p => p.providerId)).to.contain.members([
145+
'google.com',
146+
'password'
147+
]);
148+
149+
// Sign out and sign in again
150+
await auth.signOut();
151+
await signInWithEmailAndPassword(auth, email, 'password');
152+
expect(auth.currentUser!.providerData.length).to.eq(2);
153+
expect(
154+
auth.currentUser!.providerData.map(p => p.providerId)
155+
).to.contain.members(['google.com', 'password']);
156+
157+
// Update email, then sign out/sign in again
158+
const newEmail = randomEmail();
159+
await updateEmail(auth.currentUser!, newEmail);
160+
await auth.signOut();
161+
await signInWithEmailAndPassword(auth, newEmail, 'password');
162+
expect(auth.currentUser!.providerData.length).to.eq(2);
163+
expect(
164+
auth.currentUser!.providerData.map(p => p.providerId)
165+
).to.contain.members(['google.com', 'password']);
166+
});
167+
168+
it('can link with multiple idps', async () => {
169+
const googleEmail = randomEmail();
170+
const facebookEmail = randomEmail();
171+
172+
const googleCredential = GoogleAuthProvider.credential(
173+
JSON.stringify({
174+
sub: googleEmail,
175+
email: googleEmail,
176+
'email_verified': true
177+
})
178+
);
179+
180+
const facebookCredential = FacebookAuthProvider.credential(
181+
JSON.stringify({
182+
sub: facebookEmail,
183+
email: facebookEmail
184+
})
185+
);
186+
187+
// Link and then test everything
188+
const { user } = await signInWithCredential(auth, facebookCredential);
189+
await linkWithCredential(user, googleCredential);
190+
expect(user.email).to.eq(facebookEmail);
191+
expect(user.emailVerified).to.be.false;
192+
expect(user.providerData.length).to.eq(2);
193+
expect(
194+
user.providerData.find(p => p.providerId === 'google.com')!.email
195+
).to.eq(googleEmail);
196+
expect(
197+
user.providerData.find(p => p.providerId === 'facebook.com')!.email
198+
).to.eq(facebookEmail);
199+
200+
// Unlink Google and check everything again
201+
await unlink(user, ProviderId.GOOGLE);
202+
expect(user.email).to.eq(facebookEmail);
203+
expect(user.emailVerified).to.be.false;
204+
expect(user.providerData.length).to.eq(1);
205+
expect(user.providerData[0].email).to.eq(facebookEmail);
206+
expect(user.providerData[0].providerId).to.eq('facebook.com');
207+
});
208+
209+
it('IdP account takes over unverified email', async () => {
210+
const credential = GoogleAuthProvider.credential(oauthIdToken);
211+
const { user: emailUser } = await createUserWithEmailAndPassword(
212+
auth,
213+
email,
214+
'password'
215+
);
216+
217+
// Check early state
218+
expect(emailUser.emailVerified).to.be.false;
219+
220+
// Sign in with the credential and expect auto-linking
221+
const { user: googleUser } = await signInWithCredential(auth, credential);
222+
expect(googleUser.uid).to.eq(emailUser.uid);
223+
expect(googleUser.emailVerified).to.be.true;
224+
expect(auth.currentUser).to.eq(googleUser);
225+
console.log(googleUser.providerData);
226+
expect(googleUser.providerData.length).to.eq(1);
227+
expect(auth.currentUser!.providerData[0].providerId).to.eq('google.com');
228+
229+
// Signing in with password no longer works
230+
await expect(
231+
signInWithEmailAndPassword(auth, email, 'password')
232+
).to.be.rejectedWith(FirebaseError, 'auth/wrong-password');
233+
});
234+
235+
it('IdP accounts automatically link with verified emails', async () => {
236+
const googleCredential = GoogleAuthProvider.credential(
237+
JSON.stringify({
238+
sub: email,
239+
email,
240+
'email_verified': true
241+
})
242+
);
243+
244+
const githubCredential = GithubAuthProvider.credential(
245+
JSON.stringify({
246+
sub: email,
247+
email,
248+
'email_verified': true
249+
})
250+
);
251+
252+
// First sign in with Google
253+
const { user: initialUser } = await signInWithCredential(
254+
auth,
255+
googleCredential
256+
);
257+
expect(initialUser.providerData.length).to.eq(1);
258+
expect(initialUser.providerData[0].providerId).to.eq('google.com');
259+
260+
await auth.signOut();
261+
262+
// Now with GitHub
263+
const { user: githubUser } = await signInWithCredential(
264+
auth,
265+
githubCredential
266+
);
267+
expect(githubUser.uid).to.eq(initialUser.uid);
268+
expect(githubUser.providerData.length).to.eq(2);
269+
expect(githubUser.providerData.map(p => p.providerId)).to.have.members([
270+
'google.com',
271+
'github.com'
272+
]);
273+
274+
await auth.signOut();
275+
276+
// Sign in once again with the initial credential
277+
const { user: googleUser } = await signInWithCredential(
278+
auth,
279+
googleCredential
280+
);
281+
expect(googleUser.uid).to.eq(initialUser.uid);
282+
expect(googleUser.providerData.length).to.eq(2);
283+
expect(googleUser.providerData.map(p => p.providerId)).to.have.members([
284+
'google.com',
285+
'github.com'
286+
]);
287+
});
288+
});

0 commit comments

Comments
 (0)