How override ASP.NET Core Identity's password policy

C#asp.net Core-Mvcasp.net Identityasp.net Core-Identity

C# 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAdrienTorrisView Question on Stackoverflow
Solution 1 - C#AdrienTorrisView Answer on Stackoverflow
Solution 2 - C#Shashi BhatView Answer on Stackoverflow
Solution 3 - C#VitalyView Answer on Stackoverflow
Solution 4 - C#Shriram NavaratnalingamView Answer on Stackoverflow
Solution 5 - C#ViacheslavView Answer on Stackoverflow