ASP.NET Core 2.2 - Password Requirements
This article will cover password requirements and settings for ASP.NET Core Identity. I will assume you have created a new ASP.NET Core 2.2 Razor Pages project with Individual User Accounts, updated the database with the CreateIdentitySchema migration and scaffolded the Identity UI.
Let's start by listing the requirements with their default values.
Edit Startup.cs > ConfigureServices > AddIdentity, add Password requirements:
services.AddIdentity<IdentityUser, IdentityRole>(config => { config.SignIn.RequireConfirmedEmail = true; config.User.RequireUniqueEmail = true; // Password requirements config.Password.RequireDigit = true; config.Password.RequiredLength = 6; config.Password.RequiredUniqueChars = 1; config.Password.RequireLowercase = true; config.Password.RequireNonAlphanumeric = true; config.Password.RequireUppercase = true; }) .AddEntityFrameworkStores<ApplicationDbContext>(); .AddDefaultTokenProviders();
Most of these settings need no more explanation. RequiredUniqueChars = 1 is ineffective when the other requirements are applied. I have wondered exactly which characters qualify as non-alphanumeric. My research found nonWordCharacters: "./\\()\"':,.;<>!@#$%^&*|+=[]{}`~?-".
Section references:
I feel Password.RequiredLength = 6 is too weak. I set my required length to 8. If you change the required length, you should update the StringLength attribute on the InputModel password property in Register.cshtml.cs, ChangePassword.cshtml.cs, ResetPassword.cshtml.cs and SetPassword.cshtml.cs.
Edit StringLength > ErrorMessage > MinimumLength:
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)]
When a new user registers, they must fail at least once to get feedback on the requirements.
You can add a list of the requirements where the user creates or updates their password. (Register.cshtml, ChangePassword.cshtml, ResetPassword.cshtml and SetPassword.cshtml)
Edit (Register.cshtml, ChangePassword.cshtml, ResetPassword.cshtml and SetPassword.cshtml) > Password Input form-group, add list of password requirements:
<div class="form-group"> <label asp-for="Input.Password"></label> <div id="PasswordUpperDivId" class="invalid password_requirement">At least one upper case letter.</div> <div id="PasswordLowerDivId" class="invalid password_requirement">At least one lower case letter.</div> <div id="PasswordDigitDivId" class="invalid password_requirement">At least one digit.</div> <div id="PasswordSpecialDivId" class="invalid password_requirement">At least one special character.</div> <div id="PasswordLengthDivId" class="invalid password_requirement mb-2">At least 8 characters long.</div> <input asp-for="Input.Password" class="form-control" /> <span asp-validation-for="Input.Password" class="text-danger"></span> </div>
Edit site.css, add invalid and valid password requirement classes:
.invalid.password_requirement { color: orange; } .valid.password_requirement { color: green; }
You can use a little JavaScript to update the class when the user satisfies a requirement. Notice I exclude the login and deletepersonaldata paths which have password inputs but not the list.
Edit site.js, add password requirement script:
var currentPath = window.location.pathname; if (currentPath !== '/identity/account/login' && currentPath !== '/identity/account/manage/deletepersonaldata' &&(document.querySelector('#Input_Password') || document.querySelector('#Input_NewPassword'))) { var passwordInput = document.querySelector('#Input_Password'); if (!passwordInput) { passwordInput = document.querySelector('#Input_NewPassword'); } var upper = document.querySelector('#PasswordUpperDivId'); var lower = document.querySelector('#PasswordLowerDivId'); var digit = document.querySelector('#PasswordDigitDivId'); var special = document.querySelector('#PasswordSpecialDivId'); var length = document.querySelector('#PasswordLengthDivId'); // When the user starts to type something inside the password field passwordInput.addEventListener('keyup', function () { // Validate capital letters var upperCaseLetters = /[A-Z]/g; if (passwordInput.value.match(upperCaseLetters)) { upper.classList.remove('invalid'); upper.classList.add('valid'); } else { upper.classList.remove('valid'); upper.classList.add('invalid'); } // Validate lowercase letters var lowerCaseLetters = /[a-z]/g; if (passwordInput.value.match(lowerCaseLetters)) { lower.classList.remove('invalid'); lower.classList.add('valid'); } else { lower.classList.remove('valid'); lower.classList.add('invalid'); } // Validate digit var numbers = /[0-9]/g; if (passwordInput.value.match(numbers)) { digit.classList.remove('invalid'); digit.classList.add('valid'); } else { digit.classList.remove('valid'); digit.classList.add('invalid'); } // Validate special var specials = /\W/g; if (passwordInput.value.match(specials)) { special.classList.remove('invalid'); special.classList.add('valid'); } else { special.classList.remove('valid'); special.classList.add('invalid'); } // Validate length if (passwordInput.value.length >= 8) { length.classList.remove('invalid'); length.classList.add('valid'); } else { length.classList.remove('valid'); length.classList.add('invalid'); } }); }
The user can still submit the form without meeting the requirements. You can add a simple model validation with the RegularExpression attribute on the InputModel password property in Register.cshtml.cs, ChangePassword.cshtml.cs, ResetPassword.cshtml.cs and SetPassword.cshtml.cs. The RegEx = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W]).{8,}$" includes a match for minimum length of 8 which makes the StringLength attribute redundant.
Edit InputModel > Password property, add RegularExpressionAttribute:
[Required] //[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)] [RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W]).{8,}$", ErrorMessage = "The {0} does not meet requirements.")] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; }
Now when a new user registers, they are aware of the password requirements.
Comments(0)