I'm following along the bitoftech tutorial about creating Identity and role based claims with JWT. My application user is a custom User table with int PK.
Currently, the GenerateUserIdentityAsync method just returns a weird UserId not found
error. here's my code:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
and the implementation in User
entity:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType)
{
//error on this line: CreateIdentityAsync throws error
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
return userIdentity;
}
My UserManager class is defined like so:
public class AppUserManager : UserManager<User, int>
Weirdly enough, when I debug, the instance this
in GenerateIdentityAsync
does have a UserId
property, but the base only has an id
and I wonder if that is where it's erroring out? (it doesnt sound right)
I was looking at the source code (line 80) but I can't figure out where the exception is being thrown.
The exact exception being thrown is:
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.InvalidOperationException:
UserId not found.
And stack trace isn't all that helpful (to me)
How do I find out why / where the UserId is not available?
Mode details:
My GrantResourceOwnerCredentials()
:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null) // this is NOT null
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
// this line fails
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
And the ApplicationUser
(which, in my case, is just User
)
public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public int UserId { get; set; }
public string Fullname { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
}
As you found out while debugging
IdentityUser
has anId
which in your case would represent the User's Id.You need to remove the
UserId
from yourUser
class, use the baseId
fromIdentityUser
and rename theUserId
column in your custom User table toId
.Any properties you have in your
User
class needs to also have a matching column in your user table in the database. If not then you will get the same error for properties that do not match.That would mean
Fullname
,Address
andContactNumber
must have matching column names in theAspNetUsers
table or else you will get the same error for those properties as well.I faced exact same issue. After much of a head ache I could sort out the real problem. When you change data type of the Id property of User class to string, a Guid().ToString() is assigned to the Id property in the constructor and the same value is saved to the database and Identity retrieves the user details using that value.
However, if you changed the data type of the Id property to int and did not provide a value for that property in the constructor, the Identity still tries to retrieve the User details by using the default int value (0) this causes throws the message "System.InvalidOperationException: UserId not found".
I solved this by retrieving the value from the database by command.ExecuteScalar() and assign the value to user.Id. Hope this will help some one facing similar problem.
What does your ApplicationUser class look like? What does this method look like in your application?
Taiseer's comments about GrantResourceOwnerCredentials are:
I had to add the ClaimTypes.NameIdentifier to the ClaimsIdentity to resolve a similar issue. Here is the important part of my GrantResourceOwnerCredentials method:
Server=xxxx;Initial Catalog=xxxx;;User ID=xxxx_user;Password=xxxx; where user id has a space in it as opposed to Server=xxxx;Initial Catalog=xxxx;;UserID=xxxx_user;Password=xxxx; WRONG!!!!!!! See https://www.connectionstrings.com/sql-azure/ for confirmation.
You have both
UserId
andId
properties in yourUser
class -Id
is inherited fromIdentityUser
. The problem is that you probably configuredUserId
to be the primary key forUser
.The exception you get is thrown in
ClaimsIdentityFactory.CreateAsync
method, on line 97UserManager.GetSecurityStampAsync
. As you can see,user.Id
used for retrieving a security stamp.If you look inside
UserManager.GetSecurityStampAsync
you will see that the exception you get is thrown exactly here:Thus, remove
UserId
property fromUser
class and start usingId
(inherited fromIdentityUser
) instead.