-
Notifications
You must be signed in to change notification settings - Fork 946
[Auth] Add some headless IdP tests to round out our IdP WebDriver tests #4609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2e62d6c
Add headless idp tests
sam-gc 1254c85
Formatting
sam-gc d9b36a1
Update packages-exp/auth-exp/test/integration/flows/idp.local.test.ts
sam-gc b3c4721
Update packages-exp/auth-exp/test/integration/flows/idp.local.test.ts
sam-gc 39e3f35
PR feedback
sam-gc 2ffbb5d
Merge branch 'samgho/headless-idp' of github.com:firebase/firebase-js…
sam-gc 5049397
Formatting
sam-gc 8ee60e7
Fix linter
sam-gc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
288 changes: 288 additions & 0 deletions
288
packages-exp/auth-exp/test/integration/flows/idp.local.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { | ||
Auth, | ||
createUserWithEmailAndPassword, | ||
FacebookAuthProvider, | ||
getAdditionalUserInfo, | ||
GithubAuthProvider, | ||
GoogleAuthProvider, | ||
linkWithCredential, | ||
OperationType, | ||
ProviderId, | ||
signInWithCredential, | ||
signInWithEmailAndPassword, | ||
unlink, | ||
updateEmail, | ||
updatePassword, | ||
updateProfile | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
} from '@firebase/auth-exp'; | ||
import { FirebaseError } from '@firebase/util'; | ||
import { expect, use } from 'chai'; | ||
import * as chaiAsPromised from 'chai-as-promised'; | ||
import { | ||
cleanUpTestInstance, | ||
getTestInstance, | ||
randomEmail | ||
} from '../../helpers/integration/helpers'; | ||
|
||
use(chaiAsPromised); | ||
|
||
// These tests handle OAuth sign in, but they're totally headless (they don't | ||
// use the popup/redirect flows). For testing of the popup/redirect flows, look | ||
// under the test/integration/webdriver directory. | ||
|
||
describe('Integration test: headless IdP', () => { | ||
let auth: Auth; | ||
let oauthIdToken: string; | ||
let email: string; | ||
|
||
beforeEach(() => { | ||
auth = getTestInstance(/* requireEmulator */ true); | ||
email = randomEmail(); | ||
oauthIdToken = JSON.stringify({ | ||
email, | ||
'email_verified': true, | ||
sub: `oauthidp--${email}--oauthidp` | ||
}); | ||
}); | ||
|
||
afterEach(async () => { | ||
await cleanUpTestInstance(auth); | ||
}); | ||
|
||
it('signs in with an OAuth token', async () => { | ||
const cred = await signInWithCredential( | ||
auth, | ||
GoogleAuthProvider.credential(oauthIdToken) | ||
); | ||
expect(auth.currentUser).to.eq(cred.user); | ||
expect(cred.operationType).to.eq(OperationType.SIGN_IN); | ||
|
||
// Make sure the user is setup correctly | ||
const { user } = cred; | ||
expect(user.isAnonymous).to.be.false; | ||
expect(user.emailVerified).to.be.true; | ||
expect(user.providerData.length).to.eq(1); | ||
expect(user.providerData[0].providerId).to.eq('google.com'); | ||
expect(user.providerData[0].email).to.eq(email); | ||
|
||
// Make sure the additional user info is good | ||
const additionalUserInfo = getAdditionalUserInfo(cred)!; | ||
expect(additionalUserInfo.isNewUser).to.be.true; | ||
expect(additionalUserInfo.providerId).to.eq('google.com'); | ||
}); | ||
|
||
it('allows the user to update profile', async () => { | ||
const credential = GithubAuthProvider.credential(oauthIdToken); | ||
const { user } = await signInWithCredential(auth, credential); | ||
|
||
await updateProfile(user, { | ||
displayName: 'David Copperfield', | ||
photoURL: 'http://photo.test/david.png' | ||
}); | ||
|
||
// Check everything first | ||
expect(user.displayName).to.eq('David Copperfield'); | ||
expect(user.photoURL).to.eq('http://photo.test/david.png'); | ||
|
||
await auth.signOut(); | ||
|
||
// Sign in again and double check; look at current user this time | ||
await signInWithCredential(auth, credential); | ||
expect(auth.currentUser!.displayName).to.eq('David Copperfield'); | ||
expect(auth.currentUser!.photoURL).to.eq('http://photo.test/david.png'); | ||
}); | ||
|
||
it('allows the user to change the email', async () => { | ||
const credential = FacebookAuthProvider.credential(oauthIdToken); | ||
const { user } = await signInWithCredential(auth, credential); | ||
|
||
expect(user.email).to.eq(email); | ||
expect(user.emailVerified).to.be.true; | ||
|
||
const newEmail = randomEmail(); | ||
await updateEmail(user, newEmail); | ||
|
||
// Check everything first | ||
expect(user.email).to.eq(newEmail); | ||
expect(user.emailVerified).to.be.false; | ||
|
||
await auth.signOut(); | ||
|
||
// Sign in again | ||
await signInWithCredential(auth, credential); | ||
expect(auth.currentUser!.email).to.eq(newEmail); | ||
}); | ||
|
||
it('allows the user to set a password', async () => { | ||
const credential = GoogleAuthProvider.credential(oauthIdToken); | ||
const { user } = await signInWithCredential(auth, credential); | ||
|
||
expect(user.providerData.length).to.eq(1); | ||
expect(user.providerData[0].providerId).to.eq('google.com'); | ||
|
||
// Set the password and check provider data | ||
await updatePassword(user, 'password'); | ||
expect(user.providerData.length).to.eq(2); | ||
expect(user.providerData.map(p => p.providerId)).to.contain.members([ | ||
'google.com', | ||
'password' | ||
]); | ||
|
||
// Sign out and sign in again | ||
await auth.signOut(); | ||
await signInWithEmailAndPassword(auth, email, 'password'); | ||
expect(auth.currentUser!.providerData.length).to.eq(2); | ||
expect( | ||
auth.currentUser!.providerData.map(p => p.providerId) | ||
).to.contain.members(['google.com', 'password']); | ||
|
||
// Update email, then sign out/sign in again | ||
const newEmail = randomEmail(); | ||
await updateEmail(auth.currentUser!, newEmail); | ||
await auth.signOut(); | ||
await signInWithEmailAndPassword(auth, newEmail, 'password'); | ||
expect(auth.currentUser!.providerData.length).to.eq(2); | ||
expect( | ||
auth.currentUser!.providerData.map(p => p.providerId) | ||
).to.contain.members(['google.com', 'password']); | ||
}); | ||
|
||
it('can link with multiple idps', async () => { | ||
const googleEmail = randomEmail(); | ||
const facebookEmail = randomEmail(); | ||
|
||
const googleCredential = GoogleAuthProvider.credential( | ||
JSON.stringify({ | ||
sub: googleEmail, | ||
email: googleEmail, | ||
'email_verified': true | ||
}) | ||
); | ||
|
||
const facebookCredential = FacebookAuthProvider.credential( | ||
JSON.stringify({ | ||
sub: facebookEmail, | ||
email: facebookEmail | ||
}) | ||
); | ||
|
||
// Link and then test everything | ||
const { user } = await signInWithCredential(auth, facebookCredential); | ||
await linkWithCredential(user, googleCredential); | ||
expect(user.email).to.eq(facebookEmail); | ||
expect(user.emailVerified).to.be.false; | ||
expect(user.providerData.length).to.eq(2); | ||
expect( | ||
user.providerData.find(p => p.providerId === 'google.com')!.email | ||
).to.eq(googleEmail); | ||
expect( | ||
user.providerData.find(p => p.providerId === 'facebook.com')!.email | ||
).to.eq(facebookEmail); | ||
|
||
// Unlink Google and check everything again | ||
await unlink(user, ProviderId.GOOGLE); | ||
expect(user.email).to.eq(facebookEmail); | ||
expect(user.emailVerified).to.be.false; | ||
expect(user.providerData.length).to.eq(1); | ||
expect(user.providerData[0].email).to.eq(facebookEmail); | ||
expect(user.providerData[0].providerId).to.eq('facebook.com'); | ||
}); | ||
|
||
it('IdP account takes over unverified email', async () => { | ||
const credential = GoogleAuthProvider.credential(oauthIdToken); | ||
const { user: emailUser } = await createUserWithEmailAndPassword( | ||
auth, | ||
email, | ||
'password' | ||
); | ||
|
||
// Check early state | ||
expect(emailUser.emailVerified).to.be.false; | ||
|
||
// Sign in with the credential and expect auto-linking | ||
const { user: googleUser } = await signInWithCredential(auth, credential); | ||
expect(googleUser.uid).to.eq(emailUser.uid); | ||
expect(googleUser.emailVerified).to.be.true; | ||
expect(auth.currentUser).to.eq(googleUser); | ||
console.log(googleUser.providerData); | ||
expect(googleUser.providerData.length).to.eq(1); | ||
expect(auth.currentUser!.providerData[0].providerId).to.eq('google.com'); | ||
|
||
// Signing in with password no longer works | ||
await expect( | ||
signInWithEmailAndPassword(auth, email, 'password') | ||
).to.be.rejectedWith(FirebaseError, 'auth/wrong-password'); | ||
}); | ||
|
||
it('IdP accounts automatically link with verified emails', async () => { | ||
const googleCredential = GoogleAuthProvider.credential( | ||
JSON.stringify({ | ||
sub: email, | ||
email, | ||
'email_verified': true | ||
}) | ||
); | ||
|
||
const githubCredential = GithubAuthProvider.credential( | ||
JSON.stringify({ | ||
sub: email, | ||
email, | ||
'email_verified': true | ||
}) | ||
); | ||
|
||
// First sign in with Google | ||
const { user: initialUser } = await signInWithCredential( | ||
auth, | ||
googleCredential | ||
); | ||
expect(initialUser.providerData.length).to.eq(1); | ||
expect(initialUser.providerData[0].providerId).to.eq('google.com'); | ||
|
||
await auth.signOut(); | ||
|
||
// Now with GitHub | ||
const { user: githubUser } = await signInWithCredential( | ||
auth, | ||
githubCredential | ||
); | ||
expect(githubUser.uid).to.eq(initialUser.uid); | ||
expect(githubUser.providerData.length).to.eq(2); | ||
expect(githubUser.providerData.map(p => p.providerId)).to.have.members([ | ||
'google.com', | ||
'github.com' | ||
]); | ||
|
||
await auth.signOut(); | ||
|
||
// Sign in once again with the initial credential | ||
const { user: googleUser } = await signInWithCredential( | ||
auth, | ||
googleCredential | ||
); | ||
expect(googleUser.uid).to.eq(initialUser.uid); | ||
expect(googleUser.providerData.length).to.eq(2); | ||
expect(googleUser.providerData.map(p => p.providerId)).to.have.members([ | ||
'google.com', | ||
'github.com' | ||
]); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.