How override ASP.NET Core Identity's password policy
C#asp.net Core-Mvcasp.net Identityasp.net Core-IdentityC# Problem Overview
By default, ASP.NET Core Identity's password policy require at least one special character, one uppercase letter, one number, ...
How can I change this restrictions ?
There is nothing about that in the documentation (https://docs.asp.net/en/latest/security/authentication/identity.html)
I try to override the Identity's User Manager but I don't see which method manages the password policy.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(
DbContextOptions<SecurityDbContext> options,
IServiceProvider services,
IHttpContextAccessor contextAccessor,
ILogger<UserManager<ApplicationUser>> logger)
: base(
new UserStore<ApplicationUser>(new SecurityDbContext(contextAccessor)),
new CustomOptions(),
new PasswordHasher<ApplicationUser>(),
new UserValidator<ApplicationUser>[] { new UserValidator<ApplicationUser>() },
new PasswordValidator[] { new PasswordValidator() },
new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(),
services,
logger
// , contextAccessor
)
{
}
public class PasswordValidator : IPasswordValidator<ApplicationUser>
{
public Task<IdentityResult> ValidateAsync(UserManager<ApplicationUser> manager, ApplicationUser user, string password)
{
return Task.Run(() =>
{
if (password.Length >= 4) return IdentityResult.Success;
else { return IdentityResult.Failed(new IdentityError { Code = "SHORTPASSWORD", Description = "Password too short" }); }
});
}
}
public class CustomOptions : IOptions<IdentityOptions>
{
public IdentityOptions Value { get; private set; }
public CustomOptions()
{
Value = new IdentityOptions
{
ClaimsIdentity = new ClaimsIdentityOptions(),
Cookies = new IdentityCookieOptions(),
Lockout = new LockoutOptions(),
Password = null,
User = new UserOptions(),
SignIn = new SignInOptions(),
Tokens = new TokenOptions()
};
}
}
}
I add this user manager dependency in startup's class :
services.AddScoped<ApplicationUserManager>();
But when I'm using ApplicationUserManager in controllers, I have the error : An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[SecurityDbContext]' while attempting to activate 'ApplicationUserManager'.
EDIT: User's management works when I use the ASP.NET Core Identity's default classes, so it's not a database problem, or something like this
EDIT 2 : I found the solution, you have just to configure Identity in the startup's class. My answer gives some details.
C# Solutions
Solution 1 - C#
It's sooooo simple in the end ...
No need to override any class, you have just to configure the identity settings in your startup class, like this :
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 5;
options.Password.RequireLowercase = true;
options.Password.RequireNonLetterOrDigit = true;
options.Password.RequireUppercase = false;
});
Or you can configure identity when you add it :
services.AddIdentity<ApplicationUser, IdentityRole>(options=> {
options.Password.RequireDigit = false;
options.Password.RequiredLength = 4;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
})
.AddEntityFrameworkStores<SecurityDbContext>()
.AddDefaultTokenProviders();
AS.NET Core is definitively good stuff ...
Solution 2 - C#
You can modify these rules in IdentityConfig.cs file. The rules are defined in
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 5,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
}
Solution 3 - C#
Add the following line to the ConfigureServices method of startup.cs
services.Configure<IdentityOptions>(Configuration.GetSection(nameof(IdentityOptions)));
You can use different section name if you want
Then add settings to config. You can add multiple settings in multiple config sources, they will be merged. E.g. I put this in my appsettings.local.json file. This file is ignored by VCS thus my local settings never go live unlike if you hardcode settings and use #if debug or anything like that.
"IdentityOptions": {
"Password": {
"RequiredLength": 6,
"RequireDigit": false,
"RequiredUniqueChars": 1,
"RequireLowercase": false,
"RequireNonAlphanumeric": false,
"RequireUppercase": false
}
}
The same applies to appsettings.{Environment}.json or any other config source, so you can have different settings on dev server and live server without changing the code or use different build configuration
Solution 4 - C#
Additional Requirement:
> If you feel this password constraint is not enough, You can define your > own conditions by inheriting the > PasswordValidator class. >
Sample implementation :
public class CustomPasswordPolicy : PasswordValidator<AppUser>
{
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
IdentityResult result = await base.ValidateAsync(manager, user, password);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
if (password.ToLower().Contains(user.UserName.ToLower()))
{
errors.Add(new IdentityError
{
Description = "Password cannot contain username"
});
}
if (password.Contains("123"))
{
errors.Add(new IdentityError
{
Description = "Password cannot contain 123 numeric sequence"
});
}
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
I have override the ValidateAsync method in my class, and inside this method I am implementing my custom password policy.
Very Very Important
- The first code line within ValidateAsync()
IdentityResult result = await base.ValidateAsync(manager, user, password);
:
Validates the password according to the password rules given in the ConfigureServices method of Statup class (the one showed in the old answers for this post)
- The password validation functionality is defined by the IPasswordValidator interface in the Microsoft.AspNetCore.Identity namespace. So I need to register my ‘CustomPasswordPolicy’ class as the password validator for ‘AppUser’ objects.
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordPolicy>();
services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddIdentity<AppUser, IdentityRole>(opts =>
{
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = true;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = true;
opts.Password.RequireDigit = true;
}).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
> Offical Github Documentation of PasswordValidator.cs (for better > understanding): here >
Solution 5 - C#
simplest way for developers is
services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
only Password.RequiredLength can not be changed in this way, it still eqiual 6.