Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where are the AllowOnlyAlphanumericUserNames and UserValidator properites in ASP.Net Identity Core?

I am trying to set ASP.Net Identity Core in my project. The thing is we will be using Windows Authentication rather then forms authentication. I am able to get the Authorize tag to work on the home controller by setting these properties in the launch.settings for iisSettings:

"windowsAuthentication": true,
"anonymousAuthentication": false,

So now in my header I see my username through my layout file using this:

@User.Identity.Name

So my username in the upperright of my header looks like this:

[domain\username\]

So now the Authorize tag works. If I turn off windows auth and go back to anonymous auth I will get the unauthorized status for the page. And if go back to Windows auth I can see the home/index page again. Not really much to do with Identity yet.

Now I try to make a Create User Page which will eventually really be a "Add User" from Active Directory page. So I am trying to store the UserName in this format:

domain\username

Here is my create code:

[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserViewModel createUserModel)
    {
        if (ModelState.IsValid)
        {
            AppUser user = new AppUser
            {
                UserName = createUserModel.Name,
                Email = createUserModel.Email
            };
            IdentityResult result 
                = await _userManager.CreateAsync(user, createUserModel.Password);

            if (result.Succeeded)
            {
                return RedirectToAction("Index");
            }
            else
            {
                foreach (IdentityError error in result.Errors)
                {
                    ModelState.AddModelError("", error.Description);
                }
            }
        }
        return View(createUserModel);
    }

But I get back this message:

User name 'domain\username' is invalid, can only contain letters or digits.

So through reading I keep seeing that you can use these properties off of the UserManager to take care of this:

AllowOnlyAlphanumericUserNames
UserValidator

But these properties don't seem to exist in Identity Core. Is this true? How can I fix this problem in Core? Very stuck. Can't find the property from default implementations or if I try to create Custom User Validators.


Update 1:

I register my Custom User Validator in startup.cs

services.AddTransient<IUserValidator<AppUser>, CustomUserValidator>();

Here is my first try:

public class CustomUserValidator : UserValidator<AppUser>
{
    public async override Task<IdentityResult> ValidateAsync(
        UserManager<AppUser> manager, AppUser user)
    {
        IdentityResult result = await base.ValidateAsync(manager, user);
        // Allow '\' char for active directory userNames: domain\userName.
        //    Default does not allow special chars.

        foreach (IdentityError error in result.Errors)
        {
            if (error.Code == "InvalidUserName")
            {
                ((List<IdentityError>)result.Errors).Remove(error);
            }
        }

        return result.Errors.Count() == 0 ? IdentityResult.Success
            : IdentityResult.Failed(result.Errors.ToArray());
    }
}

At the end of the foreach the error count is indeed 0. But then the return is skipped over and it jumps back to the call in my account controller and the error is still there:

IdentityResult result 
                = await _userManager.CreateAsync(user, createUserModel.Password);

So my error still gets returned in the model state like this:

if (result.Succeeded)
{
    return RedirectToAction("Index");
}
else
{
    foreach (IdentityError error in result.Errors)
    {
         ModelState.AddModelError("", error.Description);
     }
 }

Here is the complete Create POST:

    [HttpPost]
    public async Task<IActionResult> CreateUser(CreateUserViewModel createUserModel)
    {
        if (ModelState.IsValid)
        {
            AppUser user = new AppUser
            {
                UserName = createUserModel.Name,
                Email = createUserModel.Email
            };
            IdentityResult result 
                = await _userManager.CreateAsync(user, createUserModel.Password);

            if (result.Succeeded)
            {
                return RedirectToAction("Index");
            }
            else
            {
                foreach (IdentityError error in result.Errors)
                {
                    ModelState.AddModelError("", error.Description);
                }
            }
        }
        return View(createUserModel);
    }

Next I try a simpler version of a custom user validator like this:

public class CustomUserValidator : IUserValidator<AppUser>
{
    public Task<IdentityResult> ValidateAsync(
        UserManager<AppUser> manager, AppUser user)
    {
        return Task.FromResult(IdentityResult.Success);
    }
}

Don't do anything. Just return a forced Success. It all goes through but when it returns to the call the Result is still IdentityResult.Failure with the "InvalideCode" You cannot have special characters in the username.

This is kicking my ass. No reason for this. This should work. Why is this so incredibly inflexible and unworkable?

This is making me look very foolish at work for using Core and I'll probably get fired for this fbs.

Update 2:

I try to put it right in the controller and it still doesn't work.

    [HttpPost]
    public async Task<IActionResult> CreateUser(CreateUserViewModel createUserModel)
    {
        if (ModelState.IsValid)
        {
            AppUser user = new AppUser
            {
                UserName = createUserModel.Name,
                Email = createUserModel.Email
            };
            IdentityResult result 
                = await _userManager.CreateAsync(user, createUserModel.Password);

            foreach (IdentityError error in result.Errors)
            {
                if (error.Code == "InvalidUserName")
                {
                    ((List<IdentityError>)result.Errors).Remove(error);
                }
            }
            if (result.Errors.Count() == 0)
            {
                result = IdentityResult.Success;
            }

            if (result.Succeeded)
            {
                return RedirectToAction("Index");
            }
            else
            {
                foreach (IdentityError error in result.Errors)
                {
                    ModelState.AddModelError("", error.Description);
                }
            }
        }
        return View(createUserModel);
    }

It kicks out right after:

IdentityResult result 
                = await _userManager.CreateAsync(user, createUserModel.Password);

and gives 500 error.

like image 878
Sam Avatar asked Mar 12 '17 14:03

Sam


1 Answers

To change what characters are allowed in a use name, add/update this line in the options you are passing to the AddIdentity function (assuming that you are doing it this way) in the StartUp class. Below...

options.User.AllowedUserNameCharacters = "";

OR

options.User.AllowedUserNameCharacters = null;

Set it to an empty string or null if you want to skip the validation on the user name.

public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        ...

        services.AddIdentity<YourUser, YourRole>(options =>
        {
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = true;
            options.User.AllowedUserNameCharacters = null; // HERE!
            options.User.RequireUniqueEmail = true;
            ...
        })
    }

Hope this helps you out.

UserValidator.cs

IdentityConfig.cs

like image 128
R. Richards Avatar answered Dec 09 '22 07:12

R. Richards