Skip to content

Commit 95b6f4d

Browse files
Fixes firebaseui.auth.idp.getAuthCredential credential initializer which was (#125)
breaking account linking flow. Added login_hint to Google OAuth flow when the email is specified. This is applied in the following 2 cases: 1. Account linking is triggered and the user needs to sign in to existing Google account. To force the user to sign in to the expected Google account, we pass the login hint with the email. 2. If a user selects email login and the Google account already exists for that email, when clicking continue to sign in with Google account, we pass the login_hint for that account.
1 parent fd1b7c3 commit 95b6f4d

File tree

9 files changed

+185
-54
lines changed

9 files changed

+185
-54
lines changed

javascript/utils/idp.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,18 @@ firebaseui.auth.idp.getAuthCredential = function(credentialObject) {
6666
if (credentialObject['secret'] && credentialObject['accessToken']) {
6767
credentialObject['oauthToken'] = credentialObject['accessToken'];
6868
credentialObject['oauthTokenSecret'] = credentialObject['secret'];
69+
return firebase.auth[firebaseui.auth.idp.AuthProviders[providerId]]
70+
.credential(credentialObject['accessToken'],
71+
credentialObject['secret']);
72+
} else if (providerId == firebase.auth.GoogleAuthProvider.PROVIDER_ID) {
73+
return firebase.auth[firebaseui.auth.idp.AuthProviders[providerId]]
74+
.credential(credentialObject['idToken'],
75+
credentialObject['accessToken']);
76+
} else {
77+
// GitHub and Facebook.
78+
return firebase.auth[firebaseui.auth.idp.AuthProviders[providerId]]
79+
.credential(credentialObject['accessToken']);
6980
}
70-
return firebase.auth[firebaseui.auth.idp.AuthProviders[providerId]]
71-
.credential(credentialObject);
7281
}
7382
return null;
7483
};

javascript/utils/idp_test.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function setUp() {
3333
firebase.auth = {};
3434
for (var providerId in firebaseui.auth.idp.AuthProviders) {
3535
firebase.auth[firebaseui.auth.idp.AuthProviders[providerId]] = {
36+
'PROVIDER_ID': providerId,
3637
'credential': goog.testing.recordFunction(function() {
3738
// Return something.
3839
return providerId;
@@ -64,15 +65,17 @@ function testIsSupportedProvider() {
6465
/**
6566
* Asserts the credential is initialized with correct OAuth response.
6667
* @param {!Object} provider The provider object.
67-
* @param {!Object} oauthResponse The response used to initialize the
68+
* @param {!Array<string>} oauthParams The OAuth params used to initialize the
6869
* credential.
6970
* @param {!Object} ref The credential reference.
7071
*/
71-
function assertCredential(provider, oauthResponse, ref) {
72+
function assertCredential(provider, oauthParams, ref) {
7273
assertNotNullNorUndefined(ref);
73-
var parameter = provider.credential.getLastCall().getArgument(0);
74-
for (var key in oauthResponse) {
75-
assertEquals(oauthResponse[key], parameter[key]);
74+
var parameters = provider.credential.getLastCall().getArguments();
75+
assertEquals(oauthParams.length, parameters.length);
76+
// Confirm the expected parameters passed when initializing the credential.
77+
for (var i = 0; i < parameters.length; i++) {
78+
assertEquals(oauthParams[i], parameters[i]);
7679
}
7780
}
7881

@@ -86,10 +89,7 @@ function testGetAuthCredential_google() {
8689
var ref = firebaseui.auth.idp.getAuthCredential(cred);
8790
assertCredential(
8891
firebase.auth.GoogleAuthProvider,
89-
{
90-
'idToken': 'ID_TOKEN',
91-
'accessToken': 'ACCESS_TOKEN'
92-
},
92+
['ID_TOKEN', 'ACCESS_TOKEN'],
9393
ref);
9494
}
9595

@@ -102,9 +102,7 @@ function testGetAuthCredential_facebook() {
102102
var ref = firebaseui.auth.idp.getAuthCredential(cred);
103103
assertCredential(
104104
firebase.auth.FacebookAuthProvider,
105-
{
106-
'accessToken': 'ACCESS_TOKEN'
107-
},
105+
['ACCESS_TOKEN'],
108106
ref);
109107
}
110108

@@ -118,10 +116,7 @@ function testGetAuthCredential_twitter() {
118116
var ref = firebaseui.auth.idp.getAuthCredential(cred);
119117
assertCredential(
120118
firebase.auth.TwitterAuthProvider,
121-
{
122-
'oauthToken': 'ACCESS_TOKEN',
123-
'oauthTokenSecret': 'SECRET'
124-
},
119+
['ACCESS_TOKEN', 'SECRET'],
125120
ref);
126121
}
127122

@@ -134,9 +129,7 @@ function testGetAuthCredential_github() {
134129
var ref = firebaseui.auth.idp.getAuthCredential(cred);
135130
assertCredential(
136131
firebase.auth.GithubAuthProvider,
137-
{
138-
'accessToken': 'ACCESS_TOKEN'
139-
},
132+
['ACCESS_TOKEN'],
140133
ref);
141134
}
142135

javascript/utils/pendingemailcredential_test.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ function setUp() {
3636
// Mock credential.
3737
firebase['auth'] = firebase['auth'] || {
3838
'GoogleAuthProvider' : {
39-
'credential' : function(response) {
40-
return response;
41-
}
39+
'credential' : function(idToken, accessToken) {
40+
assertEquals(credential['idToken'], idToken);
41+
assertEquals(credential['accessToken'], accessToken);
42+
return credential;
43+
},
44+
'PROVIDER_ID': 'google.com'
4245
}
4346
};
4447
credential = {

javascript/widgets/handler/common.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,11 @@ firebaseui.auth.widget.handler.common.isCredentialExpired = function(error) {
608608
* configuration is used.
609609
* @param {!firebaseui.auth.ui.page.Base} component The current UI component.
610610
* @param {string} providerId The provider ID of the selected IdP.
611+
* @param {?string=} opt_email The optional email to try to sign in with.
611612
* @package
612613
*/
613614
firebaseui.auth.widget.handler.common.federatedSignIn = function(
614-
app, component, providerId) {
615+
app, component, providerId, opt_email) {
615616
var container = component.getContainer();
616617
var providerSigninFailedCallback = function(error) {
617618
// TODO: align redirect and popup flow error handling for similar errors.
@@ -642,6 +643,19 @@ firebaseui.auth.widget.handler.common.federatedSignIn = function(
642643
provider['addScope'](additionalScopes[i]);
643644
}
644645
}
646+
// If Google provider is requested and email is specified, pass OAuth
647+
// parameter login_hint with that email.
648+
if (provider &&
649+
// In case the Firebase Auth version used is too old.
650+
provider.setCustomParameters &&
651+
// Only Google supports this parameter.
652+
providerId == firebase.auth.GoogleAuthProvider.PROVIDER_ID &&
653+
// Only pass login_hint when email available.
654+
opt_email) {
655+
provider.setCustomParameters({
656+
'login_hint': opt_email
657+
});
658+
}
645659
// Redirect processor.
646660
var processRedirect = function() {
647661
app.registerPending(component.executePromiseRequest(

javascript/widgets/handler/federatedlinking.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ firebaseui.auth.widget.handler.handleFederatedLinking = function(
6161
// We sign in the user through the normal federated sign-in flow,
6262
// and the callback handler will take care of linking the
6363
// pending credential to the successfully signed in user.
64+
// Pass the email since some OAuth providers support OAuth flow
65+
// with a specified email.
6466
firebaseui.auth.widget.handler.common.federatedSignIn(app, component,
65-
providerId);
67+
providerId, email);
6668
});
6769
component.render(container);
6870
// Set current UI component.

javascript/widgets/handler/federatedlinking_test.js

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ function setPendingEmailCredentials() {
5656
function testHandleFederatedLinking() {
5757
// Add additional scopes to test they are properly passed to the sign-in
5858
// method.
59-
var expectedProvider = getExpectedProviderWithScopes();
59+
var expectedProvider = getExpectedProviderWithScopes({
60+
'login_hint': federatedAccount.getEmail()
61+
});
6062
setPendingEmailCredentials();
6163
firebaseui.auth.widget.handler.handleFederatedLinking(
6264
app, container, federatedAccount.getEmail(), 'google.com');
@@ -67,12 +69,30 @@ function testHandleFederatedLinking() {
6769
}
6870

6971

72+
function testHandleFederatedLinking_noLoginHint() {
73+
// Add additional scopes to test they are properly passed to the sign-in
74+
// method.
75+
// As this is not google.com, no customParameters will be set.
76+
var expectedProvider =
77+
getExpectedProviderWithCustomParameters('github.com');
78+
setPendingEmailCredentials();
79+
firebaseui.auth.widget.handler.handleFederatedLinking(
80+
app, container, federatedAccount.getEmail(), 'github.com');
81+
assertFederatedLinkingPage(federatedAccount.getEmail());
82+
submitForm();
83+
testAuth.assertSignInWithRedirect([expectedProvider]);
84+
return testAuth.process();
85+
}
86+
87+
7088
function testHandleFederatedLinking_popup_success() {
7189
// Test successful federated linking in popup flow.
7290
app.updateConfig('signInFlow', 'popup');
7391
// Add additional scopes to test they are properly passed to the sign-in
7492
// method.
75-
var expectedProvider = getExpectedProviderWithScopes();
93+
var expectedProvider = getExpectedProviderWithScopes({
94+
'login_hint': federatedAccount.getEmail()
95+
});
7696
setPendingEmailCredentials();
7797
firebaseui.auth.widget.handler.handleFederatedLinking(
7898
app, container, federatedAccount.getEmail(), 'google.com');
@@ -124,7 +144,9 @@ function testHandleFederatedLinking_popup_success_multipleClicks() {
124144
app.updateConfig('signInFlow', 'popup');
125145
// Add additional scopes to test they are properly passed to the sign-in
126146
// method.
127-
var expectedProvider = getExpectedProviderWithScopes();
147+
var expectedProvider = getExpectedProviderWithScopes({
148+
'login_hint': federatedAccount.getEmail()
149+
});
128150
setPendingEmailCredentials();
129151
firebaseui.auth.widget.handler.handleFederatedLinking(
130152
app, container, federatedAccount.getEmail(), 'google.com');
@@ -216,7 +238,9 @@ function testHandleFederatedLinking_noPendingCredential_popup() {
216238
function testHandleFederatedLinking_error() {
217239
// Add additional scopes to test they are properly passed to the sign-in
218240
// method.
219-
var expectedProvider = getExpectedProviderWithScopes();
241+
var expectedProvider = getExpectedProviderWithScopes({
242+
'login_hint': federatedAccount.getEmail()
243+
});
220244
setPendingEmailCredentials();
221245
firebaseui.auth.widget.handler.handleFederatedLinking(
222246
app, container, federatedAccount.getEmail(), 'google.com');
@@ -237,7 +261,9 @@ function testHandleFederatedLinking_popup_recoverableError() {
237261
app.updateConfig('signInFlow', 'popup');
238262
// Add additional scopes to test they are properly passed to the sign-in
239263
// method.
240-
var expectedProvider = getExpectedProviderWithScopes();
264+
var expectedProvider = getExpectedProviderWithScopes({
265+
'login_hint': federatedAccount.getEmail()
266+
});
241267
setPendingEmailCredentials();
242268
firebaseui.auth.widget.handler.handleFederatedLinking(
243269
app, container, federatedAccount.getEmail(), 'google.com');
@@ -268,7 +294,9 @@ function testHandleFederatedLinking_popup_userCancelled() {
268294
app.updateConfig('signInFlow', 'popup');
269295
// Add additional scopes to test they are properly passed to the sign-in
270296
// method.
271-
var expectedProvider = getExpectedProviderWithScopes();
297+
var expectedProvider = getExpectedProviderWithScopes({
298+
'login_hint': federatedAccount.getEmail()
299+
});
272300
setPendingEmailCredentials();
273301
firebaseui.auth.widget.handler.handleFederatedLinking(
274302
app, container, federatedAccount.getEmail(), 'google.com');
@@ -299,7 +327,9 @@ function testHandleFederatedLinking_popup_unrecoverableError() {
299327
app.updateConfig('signInFlow', 'popup');
300328
// Add additional scopes to test they are properly passed to the sign-in
301329
// method.
302-
var expectedProvider = getExpectedProviderWithScopes();
330+
var expectedProvider = getExpectedProviderWithScopes({
331+
'login_hint': federatedAccount.getEmail()
332+
});
303333
setPendingEmailCredentials();
304334
firebaseui.auth.widget.handler.handleFederatedLinking(
305335
app, container, federatedAccount.getEmail(), 'google.com');
@@ -326,7 +356,9 @@ function testHandleFederatedLinking_popup_popupBlockedError() {
326356
app.updateConfig('signInFlow', 'popup');
327357
// Add additional scopes to test they are properly passed to the sign-in
328358
// method.
329-
var expectedProvider = getExpectedProviderWithScopes();
359+
var expectedProvider = getExpectedProviderWithScopes({
360+
'login_hint': federatedAccount.getEmail()
361+
});
330362
setPendingEmailCredentials();
331363
firebaseui.auth.widget.handler.handleFederatedLinking(
332364
app, container, federatedAccount.getEmail(), 'google.com');
@@ -357,7 +389,9 @@ function testHandleFederatedLinking_popup_popupBlockedError_redirectError() {
357389
app.updateConfig('signInFlow', 'popup');
358390
// Add additional scopes to test they are properly passed to the sign-in
359391
// method.
360-
var expectedProvider = getExpectedProviderWithScopes();
392+
var expectedProvider = getExpectedProviderWithScopes({
393+
'login_hint': federatedAccount.getEmail()
394+
});
361395
setPendingEmailCredentials();
362396
firebaseui.auth.widget.handler.handleFederatedLinking(
363397
app, container, federatedAccount.getEmail(), 'google.com');
@@ -392,7 +426,9 @@ function testHandleFederatedLinking_inProcessing() {
392426
// Add additional scopes to test they are properly passed to the sign-in
393427
// method.
394428
app.updateConfig('signInOptions', signInOptionsWithScopes);
395-
var expectedProvider = getExpectedProviderWithScopes();
429+
var expectedProvider = getExpectedProviderWithScopes({
430+
'login_hint': federatedAccount.getEmail()
431+
});
396432
setPendingEmailCredentials();
397433
firebaseui.auth.widget.handler.handleFederatedLinking(
398434
app, container, federatedAccount.getEmail(), 'google.com');
@@ -421,7 +457,9 @@ function testHandleFederatedLinking_inProcessing() {
421457
function testHandleFederatedLinking_popup_cancelled() {
422458
// Test sign in with popup flow when the popup is cancelled.
423459
app.updateConfig('signInFlow', 'popup');
424-
var expectedProvider = getExpectedProviderWithScopes();
460+
var expectedProvider = getExpectedProviderWithScopes({
461+
'login_hint': federatedAccount.getEmail()
462+
});
425463
setPendingEmailCredentials();
426464
firebaseui.auth.widget.handler.handleFederatedLinking(
427465
app, container, federatedAccount.getEmail(), 'google.com');

javascript/widgets/handler/federatedsignin.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ firebaseui.auth.widget.handler.handleFederatedSignIn = function(
4545
providerId,
4646
// On submit.
4747
function() {
48+
// Pass the email since some OAuth providers support OAuth flow
49+
// with a specified email.
4850
firebaseui.auth.widget.handler.common.federatedSignIn(app, component,
49-
providerId);
51+
providerId, email);
5052
});
5153

5254
component.render(container);

0 commit comments

Comments
 (0)