Skip to content

Commit acbac90

Browse files
authored
fix: Microsoft cancelling Permission UI improvement (#775)
1 parent 39230c6 commit acbac90

File tree

7 files changed

+224
-228
lines changed

7 files changed

+224
-228
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* Improve UI error messages when cancelling permissions on MicroSoft Work Account.
2+
* Update ES6 firebase/app import.

javascript/widgets/handler/callback.js

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,31 +52,37 @@ firebaseui.auth.widget.handler.handleCallback =
5252
firebaseui.auth.widget.handler.handleCallbackResult_(app, component,
5353
result);
5454
}, function(error) {
55+
// Normalize the error.
56+
const normalizedError =
57+
firebaseui.auth.widget.handler.common.normalizeError(error);
5558
// A previous redirect operation was triggered and some error occurred.
5659
// Test for need confirmation error and handle appropriately.
5760
// For all other errors, display info bar and show sign in screen.
58-
if (error &&
61+
if (normalizedError &&
5962
// Single out need confirmation error as email-already-in-use and
6063
// credential-already-in-use will also return email and credential
6164
// and need to be handled differently.
62-
(error['code'] == 'auth/account-exists-with-different-credential' ||
63-
error['code'] == 'auth/email-already-in-use') &&
64-
error['email'] &&
65-
error['credential']) {
65+
(normalizedError['code'] ==
66+
'auth/account-exists-with-different-credential' ||
67+
normalizedError['code'] == 'auth/email-already-in-use') &&
68+
normalizedError['email'] &&
69+
normalizedError['credential']) {
6670
// Save pending email credential.
6771
firebaseui.auth.storage.setPendingEmailCredential(
6872
new firebaseui.auth.PendingEmailCredential(
69-
error['email'], error['credential']),
73+
normalizedError['email'], normalizedError['credential']),
7074
app.getAppId());
7175
firebaseui.auth.widget.handler.handleCallbackLinking_(
72-
app, component, error['email']);
73-
} else if (error && error['code'] == 'auth/user-cancelled') {
76+
app, component, normalizedError['email']);
77+
} else if (normalizedError &&
78+
normalizedError['code'] == 'auth/user-cancelled') {
7479
// Should go back to the previous linking screen. A pending email
7580
// should be present, otherwise there's an error.
76-
var pendingCredential =
81+
const pendingCredential =
7782
firebaseui.auth.storage.getPendingEmailCredential(app.getAppId());
78-
var message =
79-
firebaseui.auth.widget.handler.common.getErrorMessage(error);
83+
const message =
84+
firebaseui.auth.widget.handler.common.getErrorMessage(
85+
normalizedError);
8086
// If there is a credential too, then the previous screen was federated
8187
// linking so we process the error as a linking flow.
8288
if (pendingCredential && pendingCredential.getCredential()) {
@@ -92,14 +98,17 @@ firebaseui.auth.widget.handler.handleCallback =
9298
} else {
9399
// Go to the sign-in page with info bar error.
94100
firebaseui.auth.widget.handler.handleCallbackFailure_(
95-
app, component, /** @type {!Error} */ (error));
101+
app, component, /** @type {!Error} */ (normalizedError));
96102
}
97-
} else if (error && error['code'] == 'auth/credential-already-in-use') {
103+
} else if (normalizedError &&
104+
normalizedError['code'] == 'auth/credential-already-in-use') {
98105
// Do nothing and keep callback UI while onUpgradeError catches and
99106
// handles this error.
100-
} else if (error &&
101-
error['code'] == 'auth/operation-not-supported-in-this-environment' &&
102-
firebaseui.auth.widget.handler.common.isPasswordProviderOnly(app)) {
107+
} else if (normalizedError &&
108+
normalizedError['code'] ==
109+
'auth/operation-not-supported-in-this-environment' &&
110+
firebaseui.auth.widget.handler.common.isPasswordProviderOnly(
111+
app)) {
103112
// Operation is not supported in this environment but only password
104113
// provider is enabled. So allow this to proceed as a no redirect result.
105114
// This will allow developers using password sign-in in Cordova to use
@@ -114,7 +123,7 @@ firebaseui.auth.widget.handler.handleCallback =
114123
} else {
115124
// Go to the sign-in page with info bar error.
116125
firebaseui.auth.widget.handler.handleCallbackFailure_(
117-
app, component, /** @type {!Error} */ (error));
126+
app, component, /** @type {!Error} */ (normalizedError));
118127
}
119128
}));
120129
};

javascript/widgets/handler/callback_test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,33 @@ function testHandleCallback_redirectError_userCancelled_noPendingCredential() {
19611961
}
19621962

19631963

1964+
function testHandleCallback_redirectError_consentRequired_invalidCredential() {
1965+
asyncTestCase.waitForSignals(1);
1966+
// Attempting to get redirect result. Special case error message equals
1967+
// consent_required. Reject with the user cancelled error.
1968+
const invalidCredentialError = {
1969+
'code': 'auth/invalid-credential',
1970+
'message': 'error=consent_required',
1971+
};
1972+
const expectedError = {
1973+
'code': 'auth/user-cancelled',
1974+
};
1975+
testAuth.assertGetRedirectResult([], null, invalidCredentialError);
1976+
// Callback rendered.
1977+
firebaseui.auth.widget.handler.handleCallback(app, container);
1978+
assertCallbackPage();
1979+
testAuth.process().then(function() {
1980+
// Redirects to the federated sign-in page.
1981+
assertProviderSignInPage();
1982+
// Confirm expected error shown in info bar.
1983+
assertInfoBarMessage(
1984+
firebaseui.auth.widget.handler.common.getErrorMessage(
1985+
expectedError));
1986+
asyncTestCase.signal();
1987+
});
1988+
}
1989+
1990+
19641991
function testHandleCallback_nullUser() {
19651992
// Test when no previous sign-in with redirect is detected and provider sign
19661993
// in page is rendered.

javascript/widgets/handler/common.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,27 @@ goog.require('goog.array');
4848
*/
4949
firebaseui.auth.OAuthResponse;
5050

51+
/**
52+
* Normalizes the error. This is useful for mapping certain errors to different
53+
* errors.
54+
* When no mapping is needed, the same error is returned.
55+
* This is currently used to map 'auth/invalid-credential' code to
56+
* 'auth/user-cancelled' when users do not grant access permission to
57+
* Microsoft work account.
58+
* @param {*} error The original error.
59+
* @return {*} The normalized error.
60+
* @package
61+
*/
62+
firebaseui.auth.widget.handler.common.normalizeError =
63+
function(error) {
64+
if (error['code'] === 'auth/invalid-credential' &&
65+
error['message'] &&
66+
error['message'].indexOf('error=consent_required') !== -1) {
67+
return {code: 'auth/user-cancelled'};
68+
}
69+
return error;
70+
};
71+
5172
/**
5273
* Sets the user as signed in with Auth result. Signs in on external Auth
5374
* instance if not already signed in and then invokes
@@ -513,7 +534,10 @@ firebaseui.auth.widget.handler.common.federatedSignIn = function(
513534
if (error['name'] && error['name'] == 'cancel') {
514535
return;
515536
}
516-
switch (error['code']) {
537+
// Normalize the error.
538+
const normalizedError =
539+
firebaseui.auth.widget.handler.common.normalizeError(error);
540+
switch (normalizedError['code']) {
517541
case 'auth/popup-blocked':
518542
// Popup blocked, switch to redirect flow as fallback.
519543
processRedirect();
@@ -533,7 +557,8 @@ firebaseui.auth.widget.handler.common.federatedSignIn = function(
533557
// For no action errors like network error, just display in info
534558
// bar in current component. A second attempt could still work.
535559
component.showInfoBar(
536-
firebaseui.auth.widget.handler.common.getErrorMessage(error));
560+
firebaseui.auth.widget.handler.common.getErrorMessage(
561+
normalizedError));
537562
break;
538563
default:
539564
// Either linking required errors or errors that are
@@ -543,7 +568,7 @@ firebaseui.auth.widget.handler.common.federatedSignIn = function(
543568
firebaseui.auth.widget.HandlerName.CALLBACK,
544569
app,
545570
container,
546-
goog.Promise.reject(error));
571+
goog.Promise.reject(normalizedError));
547572
break;
548573
}
549574
};

javascript/widgets/handler/federatedsignin_test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,42 @@ function testHandleFederatedSignIn_popup_userCancelled() {
439439
}
440440

441441

442+
function testHandleFederatedSignIn_popup_userCancelled_consentRequired() {
443+
// Test federated sign in with popup when user denies permissions on
444+
// Microsoft work account.
445+
app.updateConfig('signInFlow', 'popup');
446+
// Since Microsoft's signInOptions include a loginHintKey definition,
447+
// a login_hint should be set in the customParameters.
448+
const expectedProvider =
449+
getExpectedProviderWithCustomParameters('microsoft.com',
450+
{'login_hint': '[email protected]'});
451+
firebaseui.auth.widget.handler.handleFederatedSignIn(
452+
app, container, '[email protected]', 'microsoft.com');
453+
assertFederatedLinkingPage();
454+
submitForm();
455+
// When microsoft.com consent is rejected, auth/invalid-credential is thrown.
456+
// This will get normalized to auth/user-cancelled.
457+
const invalidCredentialError = {
458+
'code': 'auth/invalid-credential',
459+
'message': 'error=consent_required',
460+
};
461+
const expectedError = {
462+
'code': 'auth/user-cancelled',
463+
};
464+
testAuth.assertSignInWithPopup(
465+
[expectedProvider],
466+
null,
467+
invalidCredentialError);
468+
return testAuth.process().then(function() {
469+
// Remain on same page and display the error in info bar.
470+
assertFederatedLinkingPage();
471+
// Show error in info bar.
472+
assertInfoBarMessage(
473+
firebaseui.auth.widget.handler.common.getErrorMessage(expectedError));
474+
});
475+
}
476+
477+
442478
function testHandleFederatedSignIn_popup_popupBlockedError() {
443479
// Test federated sign in with popup when popup blocked.
444480
app.updateConfig('signInFlow', 'popup');

0 commit comments

Comments
 (0)