54

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.

Liam
  • 22,818
  • 25
  • 93
  • 157
AdrienTorris
  • 7,809
  • 9
  • 30
  • 49

5 Answers5

147

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

AdrienTorris
  • 7,809
  • 9
  • 30
  • 49
  • 4
    Solution added to the official documentation https://docs.asp.net/en/latest/security/authentication/identity.html – AdrienTorris Oct 04 '16 at 06:41
  • 13
    Applying your solution, it looks like in ASP .Net Core the option `options.Password.RequireNonAlphanumeric = false;` has been deprecated and divided into 2 "sub-options": `options.Password.RequireDigit = false;` and `options.Password.RequireNonAlphanumeric = false;`. – Xavier Peña Feb 07 '17 at 08:47
  • Interesting, I wanted to allow debug time password lengths of 1, but options.Password.RequiredLength = 1; did not work (tried both scenarios above). Validation still required 6 chars. Other options did work. – J W Apr 26 '19 at 13:40
  • This doesn't work. I add the top part to the bottom of my ConfigureServices method in startup.cs, I set RequiredLength to 1, and the error still states it must be between 6 and 100 characters. (.net Core 3.2) – niico May 13 '20 at 09:48
  • The reason it doesn't work with RequiredLength < 6 is because there is a hard-coded validation on the field. See https://github.com/dotnet/aspnetcore/blob/e276c8174b8bfdeb70efceafa81c75f8badbc8db/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Register.cshtml.cs at line 69. It does seem to honor the other settings if you can past that. For instance, if you set RequiredLength to 7 and only key in 6 characters, it will pass the minimum 6 check on the field, then fail with "requires 7" when you hit the Register button. – Steve In CO Jun 03 '20 at 15:54
  • The things is when I use [DataType(DataType.Password)] on a property, the ModelState is always equals true, as the password validation is not handle. Do you have an idea why? – Cedric Arnould Mar 26 '21 at 23:13
  • Is there any workaround to bypass the hardcoded minimum length of 6 characters, so it'd allow 4 chars? – kerzek May 26 '21 at 21:10
4

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,
    };
}
Thomas
  • 422
  • 5
  • 17
2

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

2

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.

Christopher Moore
  • 8,731
  • 9
  • 17
  • 33
Viacheslav
  • 446
  • 4
  • 12
0

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

Vitaly
  • 1,733
  • 13
  • 20