Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot login on ASP.NET Identity 2 site after programmatic user creation

I have a new MVC 5 razor, EF 6 web application using ASP.NET Identity 2 for the membership system. When I create the users manually using the Register link on the web page, it goes fine. I can create the user, I can then login with the given password and then logout.

I don't know how to use the database initializer with migration for Identity 2, there are countless examples with Identity 1 and other alpha and beta versions which only serve to confuse people. Since I don't know that yet, I use a temporary MVC view to install the membership.

I see the the view executes properly, I see the users and roles as well as the associations of users with roles in the database. I also see that the users have a hashed password in the record.

However, after doing that I cannot login to the identity system (local) with the plain text passwords I used in the Create method, why? BTW I have omitted the try/catch and checks for user and role creations (they execute without error).

DbContext ctx = ApplicationDbContext.Create();
transaction = ctx.Database.BeginTransaction();
RoleManager<IdentityRole> roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(ctx));
var roleAdmin = roleManager.Create(new IdentityRole("Admin"));

var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ctx));
ApplicationUser userAdmin = new ApplicationUser { Id = "admin", Email = "[email protected]", UserName = "admin" };
            userManager.Create(userAdmin, "Test_2013");
userManager.AddToRole(userAdmin.Id, "Admin");
userManager.Update(userAdmin);

transaction.Commit(); 

So after that if I attempt to login to the account with the email address and the Test_2013 password I get an error indicating the username/password is incorrect.

like image 496
Lord of Scripts Avatar asked Jun 13 '14 22:06

Lord of Scripts


3 Answers

After much investigations on the actual database (Identity 2) and the web I came to the conclusion nobody knew :) In fact of the millions of sites that have outdated information about Identity and even place Identity 2.0 code that is already outdated I had to dig into it further with the SQL Profiler and the SQL Management Studio.

In Identity 2.0 there is an Id property that is an nvarchar() but actually contains a Guid. I wonder why Microsoft didn't just made it a uniqueidentifier type?! I was setting this property when I should have left it alone (let it autogenerate it).

Likewise in Identity 2.0 there is an UserName field which I was populating with the username. It seems UserName should be the same as Email, otherwise attempts to login will simply fail.

like image 91
Lord of Scripts Avatar answered Nov 15 '22 00:11

Lord of Scripts


I also faced this stressing issue. It has to be with the default Login method created in the AccountsController:

 public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

You can see that

"await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);"

is including the Email as the first parameter, but if you check the definition for PasswordSignInAsync you will see that it MUST receive the Username as first parameter and not the Email.

It works by default because the method for Registering a New User sets both properties (Username and Email) equals to the user provided email value. But if you saved a new user programatically and set Username and Email to different values, Login will fail.

This is by all means an error from --> MS <--"I trusted those guys and spent several hours trying to make it work, until I doubted about them"

So you know now what to do: 1)Change your login method to pass username as first argument ir change Register method to save username and email with different values. I hope it helps someone else.

like image 24
Georgggg Avatar answered Nov 15 '22 00:11

Georgggg


There is a mistake in the LoginViewModel. If you look at the Login method (post version), the first parameter of the PasswordSignInAsync method should be the username, but in the loginViewModel we have the email instead.

Obviously, you cannot make it work properly, because PasswordSignInAsync is actually considering the first parameter as the username, and not the email address, and there is an email validator For the Email property in the LoginViewModel.

You have to rename the Email Property of the LoginViewModel to UserName for example, and remove the EmailAddress annotation.

Then in the corresponding login view, you have to replace the email input field and use the UserName instead.

Then in the login method in the AccountController, you use the model.UserName as the first parameter, and it should work correctly now.

like image 10
Daniel Avatar answered Nov 14 '22 23:11

Daniel