0
votes

I am wondering why the current implementation in the ASP MVC, with individual user accounts, uses

AccountController => Login()

var user = await UserManager.FindAsync(model.Email, model.Password);

user manager api

public virtual Task<TUser> FindAsync(string userName, string password);

The login by default asks for a email address and password

login form

Yet is specifically finds the user by the username and not the email address.

By default the username is automatically set to add the users email address as the username in the register form, when you create a new ApplicationUser()

AccountController => Register()

var user = new ApplicationUser() { UserName = model.Email, Email = model.Email};

I however do not want my users usernames to be their email address. I have created custom properties and changed the username field provided by Identity

AccountController => Register() // With custom edits

var user = new ApplicationUser() { UserName = model.UserDetail.UserName, Email = model.Email, UserDetailId = userDetail.UserDetailId };

If I then try login to the site once i have registered, I get an invalid username or password, as shown in the image above. The ViewModel for the login specifies that an email address be added, with a [EmailAddress] attribute . So the code is strict in using an email address, but then finds it by username.

I am thinking of changing the login from FindAsync() to FindByEmailAsync() and then add a string password as a parameter. However I do not want to mess around in the UserManager as I am not sure of the security implications and am not finding much documentation on Identity.

Ideally I would like the user to either login with their email address || username.

I basically just want to know if there are security implications as to why it was created in this manner and if there is a best practice within Identity to change the current behavior so that is looks for the actual email address when logging in?

2
This is just the default template for Internet Application. If you choose to create a blank / empty MVC application, then you can have complete control over everything you asked in your question. The template is just to get you started. - danludwig
There is so much code already written though, with confirm email, Oauth, reset password and the userManager stuff. I dont want to sound too lazy or anything but having the project build all the identity stuff and template out some of the views etc is quite helpful. I dont think i understand identity enough to start from scratch - user1752532

2 Answers

1
votes

You can do everything you asked about in your question. The biggest security risk you are going to take involves whether or not your site uses an SSL certificate, because if it does not, the password travels over the network (Internet) as plain text. Once the password is inside your call stack though, feel free to use it as a method argument. Just don't do anything silly like log it or anything like that.

UserManager is just a class that implements a few interfaces. Ultimately, it is not Microsoft's code, it is your app's code. So mold it to fit your app requirements as necessary. You can easily find a user by either username or email, if that's what you want to do. But to do that, you need to make sure no 2 user accounts can exist with the same email address. I think that's why the out of the box template just uses the email as the username, because it's just plain simpler to enforce that uniqueness constraint.

0
votes

Just thought i would add the code that worked for me if maybe some lost soul is struggling a bit. Which might be me in a few months ;)

For the work in the login actionResult

public async Task Login() // default code

if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.Email, model.Password);
            if (user != null)
            {
                await SignInAsync(user, model.RememberMe);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }

So it just checks the model.Email against UserManager() username ( !Not email )

The first change i made was to remove the [EmailAddress] Attribute on the LoginViewModel in the AccountViewModels.cs So now a user can just add text which will work for the username.

public async Task Login() // new code

I changed the var name to truly represent it ;) and added one to find the email address

var userName = await UserManager.FindAsync(model.Email, model.Password);
var userEmail = await UserManager.FindByEmailAsync(model.Email);

I then checked to see if userName is null because i still want to use that in SignInAsync() This could probably be better as a switch/case with the last if -.-

if (userName == null)
{
    userName = await UserManager.FindByEmailAsync(model.Email);
 }

And then added new values based on the email that was entered. This of cause only happens if an email address it entered.

And lastly just tweeked the other if statement to include either or for which method the user chooses

if (userName != null)
{
    await SignInAsync(userName, model.RememberMe);
    return RedirectToLocal(returnUrl);
}

If you want to send the user to his profile page it can be edited like so

if (userName != null )
{
     await SignInAsync(userName, model.RememberMe);
     return RedirectToAction("[ACTION]", "[CONTROLLER]", new { loggedInUser = userName.UserName});
}

With the users profile on a url like

playerdetails/userdetails?loggedInUser="USERNAME"