Search code examples
c#entity-frameworkvalidationiidentity

Is there a way to pass user information to an IIdentityValidator?


I'm working on a custom password validation that will do a bunch of extra checks, ideally including that the password the user is trying to create doesn't contain any permutations of their username.

We're using the Identity Framework, and my original intent was to just extend IIdentityValidator like this:

public class StrongPasswordValidator : IIdentityValidator<string>
{
    public int MinimumLength { get; set; }
    public int MaximumLength { get; set; }

    public void CustomPasswordValidator(int minimumLength, int maximumLength)
    {
        this.MinimumLength = minimumLength;
        this.MaximumLength = maximumLength;
    }

    public Task<IdentityResult> ValidateAsync(string item)
    {
        if (item.Length < MinimumLength)
        {
            return Task.FromResult(IdentityResult.Failed("Password must be a minimum of " + MinimumLength + " characters."));
        }
        if (item.Length > MaximumLength)
        {
            return Task.FromResult(IdentityResult.Failed("Password must be a maximum of " + MaximumLength + " characters."));
        }
        return Task.FromResult(IdentityResult.Success);
    }
}

But that can only take a string as an argument per the UserManager. Is there any way to pull in the username/a custom object here as well, or otherwise compare the password string to the username before persisting the information?

EDIT

For context, this is how the password validator is set and used.

UserManager:

public CustomUserManager(IUserStore<User> store,
        IPasswordHasher passwordHasher,
        IIdentityMessageService emailService)
        : base(store)
    {
        //Other validators
        PasswordValidator = new StrongPasswordValidator
        {
            MinimumLength = 8,
            MaximumLength = 100
        };
        PasswordHasher = passwordHasher;
        EmailService = emailService;
    }

User creation:

var userManager = new CustomUserManager(userStore,
                new BCryptHasher(), emailService.Object);    

userManager.CreateAsync(user, Password);

Solution

  • Instead of using the PasswordValidator use the UserValidator which will allow you to use your user object as the argument. The type for ValidateAsync comes from the generic parameter.

    public class MyUserValidator : IIdentityValidator<User>
    {
        public Task<IdentityResult> ValidateAsync(User item)
        {
            ...
        }
    }
    

    In your usermanager register it...

    public ApplicationUserManager(IUserStore<User> store)
            : base(store)
    {
        UserValidator = new MyUserValidator<User>(...);
    }