Skip to content
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

Update password reset handler to handle password policy error #1047

Merged
merged 4 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions javascript/widgets/handler/actioncode.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,27 +129,36 @@ firebaseui.auth.widget.handler.resetPassword_ = function(
*/
firebaseui.auth.widget.handler.handlePasswordResetFailure_ = function(
app, container, opt_component, opt_error) {
var errorCode = opt_error && opt_error['code'];
if (errorCode == 'auth/weak-password') {
const errorCode = opt_error && opt_error['code'];
if (errorCode === 'auth/weak-password') {
// Handles this error differently as it just requires to display a message
// to the user to use a longer password.
var errorMessage =
const errorMessage =
firebaseui.auth.widget.handler.common.getErrorMessage(opt_error);
firebaseui.auth.ui.element.setValid(
opt_component.getNewPasswordElement(), false);
firebaseui.auth.ui.element.show(
opt_component.getNewPasswordErrorElement(), errorMessage);
opt_component.getNewPasswordElement().focus();
return;
}

if (opt_component) {
opt_component.dispose();
} else if (errorCode === 'auth/password-does-not-meet-requirements') {
// Pass the error message from the backend which contains all the password
// requirements to be met.
const errorMessage =
firebaseui.auth.widget.handler.common.getErrorMessage(opt_error);
firebaseui.auth.ui.element.setValid(
opt_component.getNewPasswordElement(), false);
firebaseui.auth.ui.element.show(
opt_component.getNewPasswordErrorElement(), errorMessage);
opt_component.getNewPasswordElement().focus();
} else {
if (opt_component) {
opt_component.dispose();
}
var component = new firebaseui.auth.ui.page.PasswordResetFailure();
component.render(container);
// Set current UI component.
app.setCurrentComponent(component);
}
var component = new firebaseui.auth.ui.page.PasswordResetFailure();
component.render(container);
// Set current UI component.
app.setCurrentComponent(component);
};


Expand Down
73 changes: 73 additions & 0 deletions javascript/widgets/handler/actioncode_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,79 @@ function testHandlePasswordReset_weakPasswordError() {
}


function testHandlePasswordReset_nonCompliantPasswordError() {
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]';
const error = {
'code': 'auth/password-does-not-meet-requirements',
'message': errorMessage
};
asyncTestCase.waitForSignals(1);
firebaseui.auth.widget.handler.handlePasswordReset(
app, container, 'PASSWORD_RESET_ACTION_CODE');
// Successful action code verification.
app.getAuth().assertVerifyPasswordResetCode(
['PASSWORD_RESET_ACTION_CODE'], '[email protected]');
app.getAuth()
.process()
.then(function() {
// Password reset page should show.
assertPasswordResetPage();

goog.dom.forms.setValue(getNewPasswordElement(), '123');
// Submit password reset form.
submitForm();
// Simulates password doesn't meet requirements.
app.getAuth().assertConfirmPasswordReset(
['PASSWORD_RESET_ACTION_CODE', '123'], null, error);
return app.getAuth().process();
})
.then(function() {
// Error message should be shown on the same page.
assertPasswordResetPage();
assertEquals(
firebaseui.auth.widget.handler.common.getErrorMessage(error),
getNewPasswordErrorMessage());
asyncTestCase.signal();
});
}


function testHandlePasswordReset_nonCompliantPassword_emptyError() {
const error = {
'code': 'auth/password-does-not-meet-requirements',
'message': ''
};
asyncTestCase.waitForSignals(1);
firebaseui.auth.widget.handler.handlePasswordReset(
app, container, 'PASSWORD_RESET_ACTION_CODE');
// Successful action code verification.
app.getAuth().assertVerifyPasswordResetCode(
['PASSWORD_RESET_ACTION_CODE'], '[email protected]');
app.getAuth()
.process()
.then(function() {
// Password reset page should show.
assertPasswordResetPage();

goog.dom.forms.setValue(getNewPasswordElement(), '123');
// Submit password reset form.
submitForm();
// Simulates password doesn't meet requirements.
app.getAuth().assertConfirmPasswordReset(
['PASSWORD_RESET_ACTION_CODE', '123'], null, error);
return app.getAuth().process();
})
.then(function() {
// Error message should be shown on the same page.
assertPasswordResetPage();
assertEquals(
firebaseui.auth.widget.handler.common.getErrorMessage(error),
getNewPasswordErrorMessage());
asyncTestCase.signal();
});
}


function testHandlePasswordReset_failToResetPassword() {
asyncTestCase.waitForSignals(1);
firebaseui.auth.widget.handler.handlePasswordReset(
Expand Down
63 changes: 63 additions & 0 deletions javascript/widgets/handler/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,69 @@ firebaseui.auth.widget.handler.common.getSignedInRedirectUrl_ =
* @package
*/
firebaseui.auth.widget.handler.common.getErrorMessage = function(error) {
// Password policy error message varies depending on the policy violations.
// Hence, we construct an error message from the strings file based on the
// error message from the backend.
if (error['code'] &&
error['code'] == 'auth/password-does-not-meet-requirements') {
const originalError = error['message'];
let minPasswordLength = '';
let maxPasswordLength = '';
let passwordNotMeetMinLength = false;
let passwordNotMeetMaxLength = false;
let passwordNotContainLowercaseLetter = false;
let passwordNotContainUppercaseLetter = false;
let passwordNotContainNumericCharacter = false;
let passwordNotContainNonAlphanumericCharacter = false;

const minLengthErrorMatch =
originalError.match(/Password must contain at least (\d+)/);
if (minLengthErrorMatch) {
passwordNotMeetMinLength = true;
minPasswordLength = minLengthErrorMatch[1];
}
const maxLengthErrorMatch =
originalError.match(/Password may contain at most (\d+)/);
if (maxLengthErrorMatch) {
passwordNotMeetMaxLength = true;
maxPasswordLength = maxLengthErrorMatch[1];
}
// This needs to be hardcoded. Checking against
// "firebaseui.auth.soy2.strings.errorPasswordNotContainLowercaseLetter().toString()"
// will return a translated string, while originalError is always in
// English.
if (originalError.includes(
'Password must contain a lower case character')) {
passwordNotContainLowercaseLetter = true;
}
if (originalError.includes(
'Password must contain an upper case character')) {
passwordNotContainUppercaseLetter = true;
}
if (originalError.includes('Password must contain a numeric character')) {
passwordNotContainNumericCharacter = true;
}
if (originalError.includes(
'Password must contain a non-alphanumeric character')) {
passwordNotContainNonAlphanumericCharacter = true;
}

return firebaseui.auth.soy2.strings
.errorMissingPasswordRequirements({
passwordNotMeetMinLength: passwordNotMeetMinLength,
minPasswordLength: minPasswordLength,
passwordNotMeetMaxLength: passwordNotMeetMaxLength,
maxPasswordLength: maxPasswordLength,
passwordNotContainLowercaseLetter: passwordNotContainLowercaseLetter,
passwordNotContainUppercaseLetter: passwordNotContainUppercaseLetter,
passwordNotContainNumericCharacter:
passwordNotContainNumericCharacter,
passwordNotContainNonAlphanumericCharacter:
passwordNotContainNonAlphanumericCharacter
})
.toString();
}

// Try to get an error message from the strings file, or fall back to the
// error message from the Firebase SDK if none is found.
var message =
Expand Down
13 changes: 13 additions & 0 deletions javascript/widgets/handler/common_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,19 @@ function testGetErrorMessage_unknownError_jsonMessage() {
}


function testGetErrorMessage_passwordPolicyError_message() {
const error = {
code: 'auth/password-does-not-meet-requirements',
message:
'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]'
};
const message = firebaseui.auth.widget.handler.common.getErrorMessage(error);
assertEquals(
'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. ]',
message);
}


function testIsPasswordProviderOnly_multipleMixedProviders() {
// Set a password and federated providers in the FirebaseUI instance
// configuration.
Expand Down
112 changes: 111 additions & 1 deletion soy/strings.soy
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,111 @@
{/template}



/** Error message for when the user tries to sign in, sign up, or reset password with a
password that is not compliant with the policy.. */
{template .errorMissingPasswordRequirements kind="text"}
{@param passwordNotMeetMinLength: bool} /** Whether to display the
passwordNotMeetMinLength
error. */
{@param minPasswordLength: string} /** The minimum password length. */
{@param passwordNotMeetMaxLength: bool} /** Whether to display the
passwordNotMeetMaxLength
error. */
{@param maxPasswordLength: string} /** The maximum password length. */
{@param passwordNotContainLowercaseLetter: bool} /** Whether to display the
passwordNotContainLowercaseLetter
error. */
{@param passwordNotContainUppercaseLetter: bool} /** Whether to display the
passwordNotContainUppercaseLetter
error. */
{@param passwordNotContainNumericCharacter: bool} /** Whether to display the
passwordNotContainNumericCharacter
error. */
{@param passwordNotContainNonAlphanumericCharacter: bool} /** Whether to display the
passwordNotContainNonAlphanumericCharacter
error. */

{call .error}
{param code: 'auth/password-does-not-meet-requirements' /}
{/call}{sp}
[{sp}
{if $passwordNotMeetMinLength and $minPasswordLength != null}
{call .errorPasswordNotMeetMinLength}
{param minPasswordLength: $minPasswordLength /}
{/call}.{sp}
{/if}
{if $passwordNotMeetMaxLength and $maxPasswordLength != null}
{call .errorPasswordNotMeetMaxLength}
{param maxPasswordLength: $maxPasswordLength /}
{/call}.{sp}
{/if}
{if $passwordNotContainLowercaseLetter}{call .errorPasswordNotContainLowercaseLetter /}.{sp}{/if}
{if $passwordNotContainUppercaseLetter}{call .errorPasswordNotContainUppercaseLetter /}.{sp}{/if}
{if $passwordNotContainNumericCharacter}{call .errorPasswordNotContainNumericCharacter /}.{sp}{/if}
{if $passwordNotContainNonAlphanumericCharacter}
{call .errorPasswordNotContainNonAlphanumericCharacter /}.{sp}
{/if}
]
{/template}


/** Error message for a password that is shorter than the minimum password length. */
{template .errorPasswordNotMeetMinLength kind="text"}
{@param minPasswordLength: string} /** The minimum password length. */
{msg desc="Error message when the user enters a password that is shorter than the minimum password
length."}
Password must contain at least {$minPasswordLength} characters
{/msg}
{/template}


/** Error message for a password that is longer than the maximum password length. */
{template .errorPasswordNotMeetMaxLength kind="text"}
{@param maxPasswordLength: string} /** The maximum password length. */
{msg desc="Error message when the user enters a password that is longer than the maximum password
length."}
Password may contain at most {$maxPasswordLength} characters
{/msg}
{/template}


/** Error message for a password that does not contain a lower case character. */
{template .errorPasswordNotContainLowercaseLetter kind="text"}
{msg desc="Error message when the user enters a password that does not contain a lower case
character."}
Password must contain a lower case character
{/msg}
{/template}


/** Error message for a password that does not contain an upper case character. */
{template .errorPasswordNotContainUppercaseLetter kind="text"}
{msg desc="Error message when the user enters a password that does not contain an upper case
character."}
Password must contain an upper case character
{/msg}
{/template}


/** Error message for a password that does not contain a numeric character. */
{template .errorPasswordNotContainNumericCharacter kind="text"}
{msg desc="Error message when the user enters a password that does not contain a numeric
character."}
Password must contain a numeric character
{/msg}
{/template}


/** Error message for a password that does not contain a non-alphanumeric character. */
{template .errorPasswordNotContainNonAlphanumericCharacter kind="text"}
{msg desc="Error message when the user enters a password that does not contain a non-alphanumeric
character."}
Password must contain a non-alphanumeric character
{/msg}
{/template}


/** Translates an error code from Firebase Auth to a user-displayable string. */
{template .error kind="text"}
{@param? code: string} /** The error code. */
Expand Down Expand Up @@ -241,7 +346,7 @@
{case 'auth/weak-password'}
{msg desc="Error message for when the user tries to sign in or sign up with a password that is
too short."}
Strong passwords have at least 6 characters and a mix of letters and numbers
The password must be at least 6 characters long
{/msg}
{case 'auth/wrong-password'}
{msg desc="Error message for incorrect password."}
Expand Down Expand Up @@ -272,6 +377,11 @@
The action code is invalid. This can happen if the code is malformed, expired, or has
already been used.
{/msg}
{case 'auth/password-does-not-meet-requirements'}
{msg desc="Error message for when the user tries to sign in, sign up, or reset password with a
password that is not compliant with the policy."}
Missing password requirements:
{/msg}
{default}
{/switch}
{/template}
Expand Down
Loading
Loading