3

When I register a user using the following code:

// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser() { UserName = model.UserName };

        IdentityResult result = await UserManager.CreateAsync(user, model.Password);

        if (result.Succeeded)
        {
            await SignInAsync(user, isPersistent: false);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            AddErrors(result);
        }

        return View(model);
    }
}

I am able to access all of the views with the [Authorize] attribute. However, if I log out and back in with the same user I get an error page with

"InvalidOperationException: UserId not found"

The error seems to be coming from my SignInAsync function at the statement:

var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

I am not adding a UserID and there is no UserId field in the user classs (its Id) or in the AspNetUsers table. The user exists in that table, but again, there is no UserId.

I have tried clearing the cookies etc, but still no luck in re-logging in. Am I doing the register wrong? I don't understand why the authentication is looking for the UserId field when it doesn't seem to exist

EDIT: When I login the first time...i get the error page. However, when I reopen the page and it reads the cookie, I have access to everything. What the heck is going on with the initial login?

here is the SignInAsync method:

 private async Task SignInAsync(ApplicationUser user, bool isPersistent)
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var _identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, _identity);
        }

inclusion:

 public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
    }
rigamonk
  • 1,179
  • 2
  • 17
  • 45
  • Could you share your User model? – Neshta Jul 07 '16 at 14:45
  • the User is an IPrinicipal implementation. i think its baked in. and the user object is an IdentityUser – rigamonk Jul 07 '16 at 14:49
  • Could you show us implementation of method SignInAsync(user, isPersistent: false);? – Kirill Bestemyanov Jul 07 '16 at 15:10
  • added. Thank you for your help, all – rigamonk Jul 07 '16 at 15:27
  • @rigamonk - You are showing two different `CreateIdentityAsync`. Which one are you referring to? You should also show what your `ApplicationUser` looks like as it may be related to http://stackoverflow.com/a/35017046/5233410. – Nkosi Jul 15 '16 at 15:34
  • The user in the provided link was using a custom login. I do not have a UserId. There are userId fields in the built in aspnet tables: AspNetUserRoles and AspNetUserRoles – rigamonk Jul 15 '16 at 15:48
  • @rigamonk. rereading your question. You say the issue is when you try to log back in. You showed the `Register` action where you use the `SignInAsync`. good. Can you show the Action used when the user logs in. – Nkosi Jul 22 '16 at 06:46
  • Two things: 1.) Please share your login action code as requested above. 2.) Do you have your Database Initializer set to `DropCreateDatabaseAlways`? – Alex Bello Jul 25 '16 at 17:08

1 Answers1

0

Edit: I am wrong about you having mentioned Dapper, so the Dapper-specific details are not relevant, but the general idea of the fix remains - your Id and UserId columns are likely not getting matched up correctly.


I ran into this exact error yesterday. I think you previously had mentioned that you're using Dapper as the data access mechanism. I think that was perhaps relevant because you've also mentioned that the table has a Userid field, but your user class has an Id property. I am guessing you may also have some code to assign a new Guid to the Id property if there's not one there. Something like this:

public ApplicationUser()
{
    Id = Guid.NewGuid().ToString();
}

This combined with how Dapper mapping works may be messing you up since the Id field will wind up with that new Guid instead of the one it is supposed to be reading from the table.

In the below sample, Dapper is unable to assign the Id field of the ApplicationUser because the DB field is named differently than the property of the class. But it will already have an Id value from its constructor. This causes later calls to FindByIdAsync() and friends to fail.

public async Task<ApplicationUser> FindByNameAsync(string userName)
{
    ApplicationUser result = null;

    using (var conn = await GetOpenDBConnection())
    {
        try
        {
            // this query is bugged with Dapper because UserId does not
            // exist on ApplicationUser.
            var queryResults = await conn.QueryAsync<ApplicationUser>(
                "SELECT UserId, UserName, Email FROM dbo.Users WHERE UserName = @UserName;",
                new { UserName = userName });

            result = queryResults.FirstOrDefault();
        }
        catch (Exception ex)
        {
                            //handle error appropriately.
            result = null;
        }
    }

    return result;
}

If it is in fact a Dapper mapping issue, you can either rename the field in the query results such as SELECT UserId as [Id], UserName, Email FROM dbo.Users WHERE UserName = @UserName, or see Randall Stutton's answer for how to map Dapper fields in a really clean way.

Community
  • 1
  • 1
NYCdotNet
  • 4,500
  • 1
  • 25
  • 27