ASP.NET Core User Options and Custom Validators

Last week’s post looked at ASP.NET Core’s password options and custom validators and this week I am going to cover the built-in user options as well as writing custom user validators.

User Options

Just as last week I am going to start off showing the default registration of identity with ASP.NET Core’s built in dependency injection which is found in the ConfigureServices function of the Startup class.

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

Using the same overload of  AddIdentity  as last week which accepts an IdentityOptions there are two built-in user options for AllowedUserNameCharacters and RequireUniqueEmail. The following example shows both options.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
     options.User.AllowedUserNameCharacters = "[email protected]";
     options.User.RequireUniqueEmail = true;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

When using AllowedUserNameCharacters make sure to include all the characters needed to make up a valid email address. My first test I left out the @ symbol and couldn’t create an account.

Custom User Validtors

The above built-in user validation options are pretty limited, but this just reflects that validation on users has standardized on email addresses for the most part. In the case that you require more specialized user validation the AddUserValidator extension to IdentityBuilder can be used in the AddIdentity chain to add custom validation based on the IUserValidator interface.

The only function on this interface is ValidateAsync. The following is an example user validator that limits users to a specific set of domains. For this example the domains are part of the validator class, but if this is something actually needed I would recommend storing the domains in a different way such as in a database or configuration.

public class UserDomainValidator<TUser> : IUserValidator<TUser> 
       where TUser : IdentityUser
{
    private readonly List<string> _allowedDomains = new List<string>
    {
        "elanderson.net",
        "test.com"
    };

    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, 
                                              TUser user)
    {
        if (_allowedDomains.Any(allowed => 
               user.Email.EndsWith(allowed, StringComparison.CurrentCultureIgnoreCase)))
        {
            return Task.FromResult(IdentityResult.Success);
        }

        return Task.FromResult(
                 IdentityResult.Failed(new IdentityError
                 {
                     Code = "InvalidDomain",
                     Description = "Domain is invalid."
                 }));
    }
}

If the user’s email address doesn’t end with one of the domains in _allowedDomains the user will fail validation and the reason will be added to the list of identity validation errors which end up being show to the user. This validation is on the server side.

The following is what identity registration looks like with the user options and custom user validation.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.User.AllowedUserNameCharacters = "abccom.";
    options.User.RequireUniqueEmail = true;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddUserValidator<UserDomainValidator<ApplicationUser>>();

Wrapping Up

Following the above you can see how easy it to add any custom user validation that might be needed. It is also nice that adding custom validation on user and passwords are very similar.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.