Skip to content

Commit 44884c7

Browse files
authored
Update password reset handler to handle password policy error (#1047)
* Update password reset handler to handle 'PASSWORD_DOES_NOT_MEET_REQUIREMENTS' error * Update the translations * pin chrome 114 to run tests * Unpin chrome 114
1 parent efc1b85 commit 44884c7

Some content is hidden

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

48 files changed

+1136
-678
lines changed

javascript/widgets/handler/actioncode.js

+21-12
Original file line numberDiff line numberDiff line change
@@ -129,27 +129,36 @@ firebaseui.auth.widget.handler.resetPassword_ = function(
129129
*/
130130
firebaseui.auth.widget.handler.handlePasswordResetFailure_ = function(
131131
app, container, opt_component, opt_error) {
132-
var errorCode = opt_error && opt_error['code'];
133-
if (errorCode == 'auth/weak-password') {
132+
const errorCode = opt_error && opt_error['code'];
133+
if (errorCode === 'auth/weak-password') {
134134
// Handles this error differently as it just requires to display a message
135135
// to the user to use a longer password.
136-
var errorMessage =
136+
const errorMessage =
137137
firebaseui.auth.widget.handler.common.getErrorMessage(opt_error);
138138
firebaseui.auth.ui.element.setValid(
139139
opt_component.getNewPasswordElement(), false);
140140
firebaseui.auth.ui.element.show(
141141
opt_component.getNewPasswordErrorElement(), errorMessage);
142142
opt_component.getNewPasswordElement().focus();
143-
return;
144-
}
145-
146-
if (opt_component) {
147-
opt_component.dispose();
143+
} else if (errorCode === 'auth/password-does-not-meet-requirements') {
144+
// Pass the error message from the backend which contains all the password
145+
// requirements to be met.
146+
const errorMessage =
147+
firebaseui.auth.widget.handler.common.getErrorMessage(opt_error);
148+
firebaseui.auth.ui.element.setValid(
149+
opt_component.getNewPasswordElement(), false);
150+
firebaseui.auth.ui.element.show(
151+
opt_component.getNewPasswordErrorElement(), errorMessage);
152+
opt_component.getNewPasswordElement().focus();
153+
} else {
154+
if (opt_component) {
155+
opt_component.dispose();
156+
}
157+
var component = new firebaseui.auth.ui.page.PasswordResetFailure();
158+
component.render(container);
159+
// Set current UI component.
160+
app.setCurrentComponent(component);
148161
}
149-
var component = new firebaseui.auth.ui.page.PasswordResetFailure();
150-
component.render(container);
151-
// Set current UI component.
152-
app.setCurrentComponent(component);
153162
};
154163

155164

javascript/widgets/handler/actioncode_test.js

+73
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,79 @@ function testHandlePasswordReset_weakPasswordError() {
201201
}
202202

203203

204+
function testHandlePasswordReset_nonCompliantPasswordError() {
205+
const errorMessage = 'Missing password requirements: [Password may contain at most 16 characters, Password must contain a lower case character, Password must contain an upper case character, Password must contain a numeric character, Password must contain a non-alphanumeric character]';
206+
const error = {
207+
'code': 'auth/password-does-not-meet-requirements',
208+
'message': errorMessage
209+
};
210+
asyncTestCase.waitForSignals(1);
211+
firebaseui.auth.widget.handler.handlePasswordReset(
212+
app, container, 'PASSWORD_RESET_ACTION_CODE');
213+
// Successful action code verification.
214+
app.getAuth().assertVerifyPasswordResetCode(
215+
['PASSWORD_RESET_ACTION_CODE'], '[email protected]');
216+
app.getAuth()
217+
.process()
218+
.then(function() {
219+
// Password reset page should show.
220+
assertPasswordResetPage();
221+
222+
goog.dom.forms.setValue(getNewPasswordElement(), '123');
223+
// Submit password reset form.
224+
submitForm();
225+
// Simulates password doesn't meet requirements.
226+
app.getAuth().assertConfirmPasswordReset(
227+
['PASSWORD_RESET_ACTION_CODE', '123'], null, error);
228+
return app.getAuth().process();
229+
})
230+
.then(function() {
231+
// Error message should be shown on the same page.
232+
assertPasswordResetPage();
233+
assertEquals(
234+
firebaseui.auth.widget.handler.common.getErrorMessage(error),
235+
getNewPasswordErrorMessage());
236+
asyncTestCase.signal();
237+
});
238+
}
239+
240+
241+
function testHandlePasswordReset_nonCompliantPassword_emptyError() {
242+
const error = {
243+
'code': 'auth/password-does-not-meet-requirements',
244+
'message': ''
245+
};
246+
asyncTestCase.waitForSignals(1);
247+
firebaseui.auth.widget.handler.handlePasswordReset(
248+
app, container, 'PASSWORD_RESET_ACTION_CODE');
249+
// Successful action code verification.
250+
app.getAuth().assertVerifyPasswordResetCode(
251+
['PASSWORD_RESET_ACTION_CODE'], '[email protected]');
252+
app.getAuth()
253+
.process()
254+
.then(function() {
255+
// Password reset page should show.
256+
assertPasswordResetPage();
257+
258+
goog.dom.forms.setValue(getNewPasswordElement(), '123');
259+
// Submit password reset form.
260+
submitForm();
261+
// Simulates password doesn't meet requirements.
262+
app.getAuth().assertConfirmPasswordReset(
263+
['PASSWORD_RESET_ACTION_CODE', '123'], null, error);
264+
return app.getAuth().process();
265+
})
266+
.then(function() {
267+
// Error message should be shown on the same page.
268+
assertPasswordResetPage();
269+
assertEquals(
270+
firebaseui.auth.widget.handler.common.getErrorMessage(error),
271+
getNewPasswordErrorMessage());
272+
asyncTestCase.signal();
273+
});
274+
}
275+
276+
204277
function testHandlePasswordReset_failToResetPassword() {
205278
asyncTestCase.waitForSignals(1);
206279
firebaseui.auth.widget.handler.handlePasswordReset(

javascript/widgets/handler/common.js

+63
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,69 @@ firebaseui.auth.widget.handler.common.getSignedInRedirectUrl_ =
383383
* @package
384384
*/
385385
firebaseui.auth.widget.handler.common.getErrorMessage = function(error) {
386+
// Password policy error message varies depending on the policy violations.
387+
// Hence, we construct an error message from the strings file based on the
388+
// error message from the backend.
389+
if (error['code'] &&
390+
error['code'] == 'auth/password-does-not-meet-requirements') {
391+
const originalError = error['message'];
392+
let minPasswordLength = '';
393+
let maxPasswordLength = '';
394+
let passwordNotMeetMinLength = false;
395+
let passwordNotMeetMaxLength = false;
396+
let passwordNotContainLowercaseLetter = false;
397+
let passwordNotContainUppercaseLetter = false;
398+
let passwordNotContainNumericCharacter = false;
399+
let passwordNotContainNonAlphanumericCharacter = false;
400+
401+
const minLengthErrorMatch =
402+
originalError.match(/Password must contain at least (\d+)/);
403+
if (minLengthErrorMatch) {
404+
passwordNotMeetMinLength = true;
405+
minPasswordLength = minLengthErrorMatch[1];
406+
}
407+
const maxLengthErrorMatch =
408+
originalError.match(/Password may contain at most (\d+)/);
409+
if (maxLengthErrorMatch) {
410+
passwordNotMeetMaxLength = true;
411+
maxPasswordLength = maxLengthErrorMatch[1];
412+
}
413+
// This needs to be hardcoded. Checking against
414+
// "firebaseui.auth.soy2.strings.errorPasswordNotContainLowercaseLetter().toString()"
415+
// will return a translated string, while originalError is always in
416+
// English.
417+
if (originalError.includes(
418+
'Password must contain a lower case character')) {
419+
passwordNotContainLowercaseLetter = true;
420+
}
421+
if (originalError.includes(
422+
'Password must contain an upper case character')) {
423+
passwordNotContainUppercaseLetter = true;
424+
}
425+
if (originalError.includes('Password must contain a numeric character')) {
426+
passwordNotContainNumericCharacter = true;
427+
}
428+
if (originalError.includes(
429+
'Password must contain a non-alphanumeric character')) {
430+
passwordNotContainNonAlphanumericCharacter = true;
431+
}
432+
433+
return firebaseui.auth.soy2.strings
434+
.errorMissingPasswordRequirements({
435+
passwordNotMeetMinLength: passwordNotMeetMinLength,
436+
minPasswordLength: minPasswordLength,
437+
passwordNotMeetMaxLength: passwordNotMeetMaxLength,
438+
maxPasswordLength: maxPasswordLength,
439+
passwordNotContainLowercaseLetter: passwordNotContainLowercaseLetter,
440+
passwordNotContainUppercaseLetter: passwordNotContainUppercaseLetter,
441+
passwordNotContainNumericCharacter:
442+
passwordNotContainNumericCharacter,
443+
passwordNotContainNonAlphanumericCharacter:
444+
passwordNotContainNonAlphanumericCharacter
445+
})
446+
.toString();
447+
}
448+
386449
// Try to get an error message from the strings file, or fall back to the
387450
// error message from the Firebase SDK if none is found.
388451
var message =

javascript/widgets/handler/common_test.js

+13
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,19 @@ function testGetErrorMessage_unknownError_jsonMessage() {
13361336
}
13371337

13381338

1339+
function testGetErrorMessage_passwordPolicyError_message() {
1340+
const error = {
1341+
code: 'auth/password-does-not-meet-requirements',
1342+
message:
1343+
'Missing password requirements: [Password must contain at least 8 characters, Password may contain at most 16 characters, Password must contain a lower case character, Password must contain an upper case character, Password must contain a numeric character, Password must contain a non-alphanumeric character]'
1344+
};
1345+
const message = firebaseui.auth.widget.handler.common.getErrorMessage(error);
1346+
assertEquals(
1347+
'Missing password requirements: [ Password must contain at least 8 characters. Password may contain at most 16 characters. Password must contain a lower case character. Password must contain an upper case character. Password must contain a numeric character. Password must contain a non-alphanumeric character. ]',
1348+
message);
1349+
}
1350+
1351+
13391352
function testIsPasswordProviderOnly_multipleMixedProviders() {
13401353
// Set a password and federated providers in the FirebaseUI instance
13411354
// configuration.

soy/strings.soy

+111-1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,111 @@
210210
{/template}
211211

212212

213+
214+
/** Error message for when the user tries to sign in, sign up, or reset password with a
215+
password that is not compliant with the policy.. */
216+
{template .errorMissingPasswordRequirements kind="text"}
217+
{@param passwordNotMeetMinLength: bool} /** Whether to display the
218+
passwordNotMeetMinLength
219+
error. */
220+
{@param minPasswordLength: string} /** The minimum password length. */
221+
{@param passwordNotMeetMaxLength: bool} /** Whether to display the
222+
passwordNotMeetMaxLength
223+
error. */
224+
{@param maxPasswordLength: string} /** The maximum password length. */
225+
{@param passwordNotContainLowercaseLetter: bool} /** Whether to display the
226+
passwordNotContainLowercaseLetter
227+
error. */
228+
{@param passwordNotContainUppercaseLetter: bool} /** Whether to display the
229+
passwordNotContainUppercaseLetter
230+
error. */
231+
{@param passwordNotContainNumericCharacter: bool} /** Whether to display the
232+
passwordNotContainNumericCharacter
233+
error. */
234+
{@param passwordNotContainNonAlphanumericCharacter: bool} /** Whether to display the
235+
passwordNotContainNonAlphanumericCharacter
236+
error. */
237+
238+
{call .error}
239+
{param code: 'auth/password-does-not-meet-requirements' /}
240+
{/call}{sp}
241+
[{sp}
242+
{if $passwordNotMeetMinLength and $minPasswordLength != null}
243+
{call .errorPasswordNotMeetMinLength}
244+
{param minPasswordLength: $minPasswordLength /}
245+
{/call}.{sp}
246+
{/if}
247+
{if $passwordNotMeetMaxLength and $maxPasswordLength != null}
248+
{call .errorPasswordNotMeetMaxLength}
249+
{param maxPasswordLength: $maxPasswordLength /}
250+
{/call}.{sp}
251+
{/if}
252+
{if $passwordNotContainLowercaseLetter}{call .errorPasswordNotContainLowercaseLetter /}.{sp}{/if}
253+
{if $passwordNotContainUppercaseLetter}{call .errorPasswordNotContainUppercaseLetter /}.{sp}{/if}
254+
{if $passwordNotContainNumericCharacter}{call .errorPasswordNotContainNumericCharacter /}.{sp}{/if}
255+
{if $passwordNotContainNonAlphanumericCharacter}
256+
{call .errorPasswordNotContainNonAlphanumericCharacter /}.{sp}
257+
{/if}
258+
]
259+
{/template}
260+
261+
262+
/** Error message for a password that is shorter than the minimum password length. */
263+
{template .errorPasswordNotMeetMinLength kind="text"}
264+
{@param minPasswordLength: string} /** The minimum password length. */
265+
{msg desc="Error message when the user enters a password that is shorter than the minimum password
266+
length."}
267+
Password must contain at least {$minPasswordLength} characters
268+
{/msg}
269+
{/template}
270+
271+
272+
/** Error message for a password that is longer than the maximum password length. */
273+
{template .errorPasswordNotMeetMaxLength kind="text"}
274+
{@param maxPasswordLength: string} /** The maximum password length. */
275+
{msg desc="Error message when the user enters a password that is longer than the maximum password
276+
length."}
277+
Password may contain at most {$maxPasswordLength} characters
278+
{/msg}
279+
{/template}
280+
281+
282+
/** Error message for a password that does not contain a lower case character. */
283+
{template .errorPasswordNotContainLowercaseLetter kind="text"}
284+
{msg desc="Error message when the user enters a password that does not contain a lower case
285+
character."}
286+
Password must contain a lower case character
287+
{/msg}
288+
{/template}
289+
290+
291+
/** Error message for a password that does not contain an upper case character. */
292+
{template .errorPasswordNotContainUppercaseLetter kind="text"}
293+
{msg desc="Error message when the user enters a password that does not contain an upper case
294+
character."}
295+
Password must contain an upper case character
296+
{/msg}
297+
{/template}
298+
299+
300+
/** Error message for a password that does not contain a numeric character. */
301+
{template .errorPasswordNotContainNumericCharacter kind="text"}
302+
{msg desc="Error message when the user enters a password that does not contain a numeric
303+
character."}
304+
Password must contain a numeric character
305+
{/msg}
306+
{/template}
307+
308+
309+
/** Error message for a password that does not contain a non-alphanumeric character. */
310+
{template .errorPasswordNotContainNonAlphanumericCharacter kind="text"}
311+
{msg desc="Error message when the user enters a password that does not contain a non-alphanumeric
312+
character."}
313+
Password must contain a non-alphanumeric character
314+
{/msg}
315+
{/template}
316+
317+
213318
/** Translates an error code from Firebase Auth to a user-displayable string. */
214319
{template .error kind="text"}
215320
{@param? code: string} /** The error code. */
@@ -241,7 +346,7 @@
241346
{case 'auth/weak-password'}
242347
{msg desc="Error message for when the user tries to sign in or sign up with a password that is
243348
too short."}
244-
Strong passwords have at least 6 characters and a mix of letters and numbers
349+
The password must be at least 6 characters long
245350
{/msg}
246351
{case 'auth/wrong-password'}
247352
{msg desc="Error message for incorrect password."}
@@ -272,6 +377,11 @@
272377
The action code is invalid. This can happen if the code is malformed, expired, or has
273378
already been used.
274379
{/msg}
380+
{case 'auth/password-does-not-meet-requirements'}
381+
{msg desc="Error message for when the user tries to sign in, sign up, or reset password with a
382+
password that is not compliant with the policy."}
383+
Missing password requirements:
384+
{/msg}
275385
{default}
276386
{/switch}
277387
{/template}

0 commit comments

Comments
 (0)