Skip to content

Commit 30e290b

Browse files
authored
Localize email action (#681)
* added mfa related email action handler * type fix * bump core sdk dep * revert closure api changed * updated changelod * fixed alignment * update package-lock * fix alignment
1 parent 0acaa0e commit 30e290b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3915
-1950
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Fixed the issue that sign-in with email and password silently fails if account was created using an email link.

javascript/ui/element/elementtesthelper.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ firebaseui.auth.ui.element.ElementTestHelper.prototype.setComponent =
5656
/**
5757
* Excludes some tests from being run.
5858
* @param {...string} var_arg Test names to exclude.
59-
* @return {firebaseui.auth.ui.element.ElementTestHelper} The test helper
59+
* @return {!firebaseui.auth.ui.element.ElementTestHelper} The test helper
6060
* itself.
6161
*/
6262
firebaseui.auth.ui.element.ElementTestHelper.prototype.excludeTests =
@@ -69,7 +69,7 @@ firebaseui.auth.ui.element.ElementTestHelper.prototype.excludeTests =
6969

7070
/**
7171
* Registers all tests related to the element into the global namespace.
72-
* @return {firebaseui.auth.ui.element.ElementTestHelper} The test helper
72+
* @return {!firebaseui.auth.ui.element.ElementTestHelper} The test helper
7373
* itself.
7474
*/
7575
firebaseui.auth.ui.element.ElementTestHelper.prototype.registerTests =

javascript/utils/eventregister.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ firebaseui.auth.EventDispatcher = class extends goog.events.EventTarget {
137137
}
138138

139139
/**
140-
* @return {Element} The element corresponding to the event dispatcher.
140+
* @return {!Element} The element corresponding to the event dispatcher.
141141
*/
142142
getElement() {
143143
return this.el_;

javascript/widgets/authui.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ firebaseui.auth.AuthUI.prototype.checkForDeprecation_ = function() {
935935

936936

937937
/**
938-
* @return {firebaseui.auth.widget.Config} The application configuration.
938+
* @return {!firebaseui.auth.widget.Config} The application configuration.
939939
*/
940940
firebaseui.auth.AuthUI.prototype.getConfig = function() {
941941
// Check if instance is already destroyed.
@@ -1032,7 +1032,7 @@ firebaseui.auth.AuthUI.prototype.showOneTapSignIn = function(handler) {
10321032
* @param {string} email The email to sign in with.
10331033
* @param {?firebaseui.auth.PendingEmailCredential=} opt_pendingCredential The
10341034
* pending credential to link to the successfully signed in user
1035-
* @return {!firebase.Promise<void>}
1035+
* @return {!firebase.Promise<void>|!goog.Promise<void>}
10361036
*/
10371037
firebaseui.auth.AuthUI.prototype.sendSignInLinkToEmail =
10381038
function(email, opt_pendingCredential) {
@@ -1043,8 +1043,9 @@ firebaseui.auth.AuthUI.prototype.sendSignInLinkToEmail =
10431043
var sid = firebaseui.auth.util.generateRandomAlphaNumericString(32);
10441044
// Assert email link sign-in allowed.
10451045
if (!this.getConfig().isEmailLinkSignInAllowed()) {
1046-
throw new Error(
1047-
'Email link sign-in should be enabled to trigger email sending.');
1046+
return goog.Promise.reject(
1047+
new Error(
1048+
'Email link sign-in should be enabled to trigger email sending.'));
10481049
}
10491050
var actionCodeSettings =/** @type {!firebase.auth.ActionCodeSettings} */ (
10501051
this.getConfig().getEmailLinkSignInActionCodeSettings());

javascript/widgets/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,8 +952,10 @@ Config.WidgetMode = {
952952
CALLBACK: 'callback',
953953
RECOVER_EMAIL: 'recoverEmail',
954954
RESET_PASSWORD: 'resetPassword',
955+
REVERT_SECOND_FACTOR_ADDITION: 'revertSecondFactorAddition',
955956
SELECT: 'select',
956957
SIGN_IN: 'signIn',
958+
VERIFY_AND_CHANGE_EMAIL: 'verifyAndChangeEmail',
957959
VERIFY_EMAIL: 'verifyEmail',
958960
};
959961

javascript/widgets/dispatcher.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ function doDispatchOperation(app, e) {
234234
getActionCode());
235235
break;
236236

237+
case Config.WidgetMode.REVERT_SECOND_FACTOR_ADDITION:
238+
handler.handle(
239+
HandlerName.REVERT_SECOND_FACTOR_ADDITION,
240+
app,
241+
container,
242+
getActionCode());
243+
break;
244+
237245
case Config.WidgetMode.VERIFY_EMAIL:
238246
handler.handle(
239247
HandlerName.EMAIL_VERIFICATION,
@@ -245,6 +253,17 @@ function doDispatchOperation(app, e) {
245253
getContinueCallback());
246254
break;
247255

256+
case Config.WidgetMode.VERIFY_AND_CHANGE_EMAIL:
257+
handler.handle(
258+
HandlerName.VERIFY_AND_CHANGE_EMAIL,
259+
app,
260+
container,
261+
getActionCode(),
262+
// Check if continue URL is available. if so, display a button to
263+
// redirect to it.
264+
getContinueCallback());
265+
break;
266+
248267
case Config.WidgetMode.SIGN_IN:
249268
// Complete signin.
250269
handler.handle(

javascript/widgets/dispatcher_test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,57 @@ testSuite({
794794
testUtil.assertGoTo(continueUrl);
795795
},
796796

797+
testDispatchOperation_verifyAndChangeEmail() {
798+
const element = dom.createElement('div');
799+
setModeAndUrlParams(
800+
Config.WidgetMode.VERIFY_AND_CHANGE_EMAIL,
801+
{'oobCode': 'ACTION_CODE'});
802+
dispatcher.dispatchOperation(app, element);
803+
assertHandlerInvoked(
804+
firebaseui.auth.widget.HandlerName.VERIFY_AND_CHANGE_EMAIL,
805+
app,
806+
element,
807+
'ACTION_CODE');
808+
},
809+
810+
testDispatchOperation_verifyAndChangeEmail_continueUrl() {
811+
const element = dom.createElement('div');
812+
const continueUrl = 'http://www.example.com/path/page?a=1#b=2';
813+
stub.replace(
814+
firebaseui.auth.util,
815+
'getCurrentUrl',
816+
() => 'http://example.firebaseapp.com/__/auth/action?mode=' +
817+
'verifyAndChangeEmail&apiKey=API_KEY&oobCode=ACTION_CODE&' +
818+
'continueUrl=' + encodeURIComponent(continueUrl));
819+
dispatcher.dispatchOperation(app, element);
820+
assertHandlerInvoked(
821+
firebaseui.auth.widget.HandlerName.VERIFY_AND_CHANGE_EMAIL,
822+
app,
823+
element,
824+
'ACTION_CODE');
825+
// Get callback passed to verify and change email handler and confirm it
826+
// redirects to continue URL.
827+
const handler =
828+
firebaseui.auth.widget.handlers_[
829+
firebaseui.auth.widget.HandlerName.VERIFY_AND_CHANGE_EMAIL];
830+
const continueCallback = handler.getLastCall().getArgument(3);
831+
continueCallback();
832+
testUtil.assertGoTo(continueUrl);
833+
},
834+
835+
testDispatchOperation_revertSecondFactorAddition() {
836+
const element = dom.createElement('div');
837+
setModeAndUrlParams(
838+
Config.WidgetMode.REVERT_SECOND_FACTOR_ADDITION,
839+
{'oobCode': 'ACTION_CODE'});
840+
dispatcher.dispatchOperation(app, element);
841+
assertHandlerInvoked(
842+
firebaseui.auth.widget.HandlerName.REVERT_SECOND_FACTOR_ADDITION,
843+
app,
844+
element,
845+
'ACTION_CODE');
846+
},
847+
797848
testDispatchOperation_resetPassword() {
798849
const element = dom.createElement('div');
799850
setModeAndUrlParams(

javascript/widgets/handler/actioncode.js

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
goog.provide('firebaseui.auth.widget.handler.handleEmailChangeRevocation');
2020
goog.provide('firebaseui.auth.widget.handler.handleEmailVerification');
2121
goog.provide('firebaseui.auth.widget.handler.handlePasswordReset');
22+
goog.provide('firebaseui.auth.widget.handler.handleRevertSecondFactorAddition');
23+
goog.provide('firebaseui.auth.widget.handler.handleVerifyAndChangeEmail');
2224

2325
goog.require('firebaseui.auth.soy2.strings');
2426
goog.require('firebaseui.auth.ui.element');
@@ -30,6 +32,10 @@ goog.require('firebaseui.auth.ui.page.PasswordRecoveryEmailSent');
3032
goog.require('firebaseui.auth.ui.page.PasswordReset');
3133
goog.require('firebaseui.auth.ui.page.PasswordResetFailure');
3234
goog.require('firebaseui.auth.ui.page.PasswordResetSuccess');
35+
goog.require('firebaseui.auth.ui.page.RevertSecondFactorAdditionFailure');
36+
goog.require('firebaseui.auth.ui.page.RevertSecondFactorAdditionSuccess');
37+
goog.require('firebaseui.auth.ui.page.VerifyAndChangeEmailFailure');
38+
goog.require('firebaseui.auth.ui.page.VerifyAndChangeEmailSuccess');
3339
goog.require('firebaseui.auth.widget.Handler');
3440
goog.require('firebaseui.auth.widget.HandlerName');
3541
goog.require('firebaseui.auth.widget.handler.common');
@@ -272,6 +278,128 @@ firebaseui.auth.widget.handler.handleEmailVerification = function(
272278
};
273279

274280

281+
/**
282+
* Handles the verify and change email action flow.
283+
*
284+
* @param {!firebaseui.auth.AuthUI} app The current Firebase UI instance whose
285+
* configuration is used.
286+
* @param {!Element} container The container DOM element.
287+
* @param {string} actionCode The verify and change email action code.
288+
* @param {?function()=} onContinueClick The optional callback to invoke when
289+
* the continue button is clicked. If not provided, no continue button is
290+
* displayed.
291+
*/
292+
firebaseui.auth.widget.handler.handleVerifyAndChangeEmail = function(
293+
app, container, actionCode, onContinueClick) {
294+
let email = null;
295+
// Gets the email related to the code.
296+
app.registerPending(
297+
app.getAuth()
298+
.checkActionCode(actionCode)
299+
.then((info) => {
300+
email = info['data']['email'];
301+
// Then applies it.
302+
return app.getAuth().applyActionCode(actionCode);
303+
})
304+
.then(
305+
() => {
306+
const component =
307+
new firebaseui.auth.ui.page.VerifyAndChangeEmailSuccess(
308+
email, onContinueClick);
309+
component.render(container);
310+
// Set current UI component.
311+
app.setCurrentComponent(component);
312+
},
313+
(error) => {
314+
const component =
315+
new firebaseui.auth.ui.page.VerifyAndChangeEmailFailure();
316+
component.render(container);
317+
// Set current UI component.
318+
app.setCurrentComponent(component);
319+
}));
320+
};
321+
322+
323+
/**
324+
* Handles the revert second factor addition email action flow.
325+
*
326+
* @param {!firebaseui.auth.AuthUI} app The current Firebase UI instance whose
327+
* configuration is used.
328+
* @param {!Element} container The container DOM element.
329+
* @param {string} actionCode The revert second factor addition action code.
330+
*/
331+
firebaseui.auth.widget.handler.handleRevertSecondFactorAddition =
332+
function(app, container, actionCode) {
333+
let email = null;
334+
let multiFactorInfo = null;
335+
app.registerPending(
336+
app.getAuth()
337+
.checkActionCode(actionCode)
338+
.then((info) => {
339+
email = info['data']['email'];
340+
multiFactorInfo = info['data']['multiFactorInfo'];
341+
// Then applies it.
342+
return app.getAuth().applyActionCode(actionCode);
343+
})
344+
.then(() => {
345+
firebaseui.auth.widget.handler
346+
.handleRevertSecondFactorAdditionSuccess_(
347+
app, container, email, multiFactorInfo);
348+
}, (error) => {
349+
const component =
350+
new firebaseui.auth.ui.page
351+
.RevertSecondFactorAdditionFailure();
352+
component.render(container);
353+
// Set current UI component.
354+
app.setCurrentComponent(component);
355+
}));
356+
};
357+
358+
359+
/**
360+
* Handles the successful revert second factor addition action.
361+
* @param {!firebaseui.auth.AuthUI} app The current Firebase UI instance whose
362+
* configuration is used.
363+
* @param {!Element} container The container DOM element.
364+
* @param {string} email The email of the acount.
365+
* @param {!firebase.auth.MultiFactorInfo} multiFactorInfo The info of
366+
* multi-factor to be unenrolled.
367+
* @private
368+
*/
369+
firebaseui.auth.widget.handler.handleRevertSecondFactorAdditionSuccess_ =
370+
function(app, container, email, multiFactorInfo) {
371+
let component = new firebaseui.auth.ui.page.RevertSecondFactorAdditionSuccess(
372+
multiFactorInfo['factorId'],
373+
() => {
374+
component.executePromiseRequest(
375+
goog.bind(app.getAuth().sendPasswordResetEmail, app.getAuth()),
376+
[email],
377+
() => {
378+
// Reset password code sent.
379+
component.dispose();
380+
component =
381+
new firebaseui.auth.ui.page.PasswordRecoveryEmailSent(
382+
email,
383+
undefined,
384+
app.getConfig().getTosUrl(),
385+
app.getConfig().getPrivacyPolicyUrl());
386+
component.render(container);
387+
// Set current UI component.
388+
app.setCurrentComponent(component);
389+
}, (error) => {
390+
// Failed to send reset password code.
391+
component.showInfoBar(
392+
firebaseui.auth.soy2.strings.errorSendPasswordReset()
393+
.toString());
394+
});
395+
},
396+
multiFactorInfo['phoneNumber']);
397+
component.render(container);
398+
// Set current UI component.
399+
app.setCurrentComponent(component);
400+
};
401+
402+
275403
// Register handlers.
276404
firebaseui.auth.widget.handler.register(
277405
firebaseui.auth.widget.HandlerName.PASSWORD_RESET,
@@ -288,3 +416,15 @@ firebaseui.auth.widget.handler.register(
288416
firebaseui.auth.widget.HandlerName.EMAIL_VERIFICATION,
289417
/** @type {firebaseui.auth.widget.Handler} */
290418
(firebaseui.auth.widget.handler.handleEmailVerification));
419+
420+
/** @suppress {missingRequire} */
421+
firebaseui.auth.widget.handler.register(
422+
firebaseui.auth.widget.HandlerName.REVERT_SECOND_FACTOR_ADDITION,
423+
/** @type {!firebaseui.auth.widget.Handler} */
424+
(firebaseui.auth.widget.handler.handleRevertSecondFactorAddition));
425+
426+
/** @suppress {missingRequire} */
427+
firebaseui.auth.widget.handler.register(
428+
firebaseui.auth.widget.HandlerName.VERIFY_AND_CHANGE_EMAIL,
429+
/** @type {!firebaseui.auth.widget.Handler} */
430+
(firebaseui.auth.widget.handler.handleVerifyAndChangeEmail));

0 commit comments

Comments
 (0)