diff --git a/README.md b/README.md index 3723ddc73a..63a4d81ea1 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,8 @@ var server = ParseServer({ // 1. a RegExp object or a regex string representing the pattern to enforce validatorPattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/, // enforce password with at least 8 char with at least 1 lower case, 1 upper case and 1 digit // 2. a callback function to be invoked to validate the password - validatorCallback: (password) => { return validatePassword(password) }, + validatorCallback: (password) => { return validatePassword(password) }, + passwordRequirementsHumanErrorMessage: 'Password must contain a capital letter, lowercase letter, a number and be at least 8 characters long.', //This error message is sent instead of the generic "Password does not meet the Password Policy requirements." message. doNotAllowUsername: true, // optional setting to disallow username in passwords maxPasswordAge: 90, // optional setting in days for password expiry. Login fails if user does not reset the password within this period after signup/last reset. maxPasswordHistory: 5, // optional setting to prevent reuse of previous n passwords. Maximum value that can be specified is 20. Not specifying it or specifying 0 will not enforce history. diff --git a/src/RestWrite.js b/src/RestWrite.js index 68050df992..033d7196e8 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -484,7 +484,19 @@ RestWrite.prototype._validatePasswordPolicy = function() { RestWrite.prototype._validatePasswordRequirements = function() { // check if the password conforms to the defined password policy if configured - const policyError = 'Password does not meet the Password Policy requirements.'; + let containsUsernameError = 'Password cannot contain your username.'; + let policyError = 'Password does not meet the Password Policy requirements.'; + + if(this.config.passwordPolicy.passwordRequirementsHumanErrorMessage){ + // If we specified a custom error in our configuration use it. + // Example: "Passwords must include a Capital Letter, Lowercase Letter, and a number." + // + // This is expesially useful on the generic "password reset" page, + // as it allows the programmer to communicate specific requirements instead of: + // a. making the user guess whats wrong + // b. making a custom password reset page that shows the requirements + policyError = this.config.passwordPolicy.passwordRequirementsHumanErrorMessage; + } // check whether the password meets the password strength requirements if (this.config.passwordPolicy.patternValidator && !this.config.passwordPolicy.patternValidator(this.data.password) || @@ -496,7 +508,7 @@ RestWrite.prototype._validatePasswordRequirements = function() { if (this.config.passwordPolicy.doNotAllowUsername === true) { if (this.data.username) { // username is not passed during password reset if (this.data.password.indexOf(this.data.username) >= 0) - return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError)); + return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); } else { // retrieve the User object using objectId during password reset return this.config.database.find('_User', {objectId: this.objectId()}) .then(results => { @@ -504,7 +516,7 @@ RestWrite.prototype._validatePasswordRequirements = function() { throw undefined; } if (this.data.password.indexOf(results[0].username) >= 0) - return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError)); + return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); return Promise.resolve(); }); }