JWT Authentication - UserManager.GetUserAsync returns null
Solution 1
UserManager.GetUserAsync
internally uses UserManager.GetUserId
to retrieve the user id of the user which is then used to query the object from the user store (i.e. your database).
GetUserId
basically looks like this:
public string GetUserId(ClaimsPrincipal principal)
{
return principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType);
}
So this returns the claim value of Options.ClaimsIdentity.UserIdClaimType
. Options
is the IdentityOptions
object that you configure Identity with. By default the value of UserIdClaimType
is ClaimTypes.NameIdentifier
, i.e. "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
.
So when you try to use UserManager.GetUserAsync(HttpContext.User)
, where that user principal has a UserID
claim, the user manager is simply looking for a different claim.
You can fix this by either switchting to the ClaimTypes.NameIdentifier
:
new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
})
Or you configure Identity properly so it will use your UserID
claim type:
// in Startup.ConfigureServices
services.AddIdentity(options => {
options.ClaimsIdentity.UserIdClaimType = "UserID";
});
Solution 2
When you create Claims, you just need to do like:
List<Claim> claims = new()
{
new Claim(ClaimTypes.NameIdentifier, user.Id), // This line is important.
new Claim(ClaimTypes.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, jti)
};
Related videos on Youtube
Alex Herman
FREELANCER PROFILE — https://talent.hubstaff.com/profiles/aliaksei-herman ASP.NET MVC + jQuery, Knockout .NET Core + SPA apps using React, Angular, Vue My 5 projects on GitHub: feasible-ui — react-based UI toolkit written from scratch | not based on other UI libraries, serves as a boilerplate providing essential controls eixample — multi-tenant ASP.NET Core architecture: EF Core + PostgreSQL + React, Vue, Angular eixample_webapi2 — same as above, only for proprietary ASP.NET: EF + SQLServer + React, Vue, Angular pern-multitenancy — multi-tenant PERN architecture mern-multitenancy — multi-tenant MERN architecture
Updated on October 19, 2021Comments
-
Alex Herman over 2 years
In
AuthController
when authenticating I create a few Claims -UserID
is one of them.... Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, user.UserName), new Claim("UserID", user.Id.ToString()), })
When Angular app makes request I am able to fetch
UserID
in another controllerClaim claimUserId = User.Claims.SingleOrDefault(c => c.Type == "UserID");
The
ControllerBase.User
instance holds.Identity
object which in turn holdsClaims
collection.Identity.IsAuthenticated
equalsTrue
.Identity.Name
holdsadmin
string (name of the relevant user).
If I try to fetch user like this:
var user = await UserManager.GetUserAsync(HttpContext.User)
the
user
isnull
.Perhaps, I forgot to add some extra claim?
Or maybe, once I'm using JWT - I should override the default
UserManager
functionality so it fetches user byclaim
which holdsUserID
?Or maybe there's a better approach?
Additional info:
The
Identity
is registered as followsservices.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders();
ApplicationUser.Id
field is ofbigint
(or in C# oflong
) typeAlso, I create users in
EF Seed Data
with UserManager which is resolved usingServiceProvider
_userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>(); ... adminUser.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(adminUser, "123qwe"); _userManager.CreateAsync(adminUser);
-
Alex Riabov almost 6 yearsCould you share how you register Identity?
-
Alex Herman almost 6 yearshi @AlexRiabov, just answered your question, the code under
Additional Info
section.
-
Rosdi Kasim over 5 yearsAlternatively..., if
userId
is not available, one can useuserManager.FindByNameAsync(username)
instead if that fits the business requirement. -
mwilson about 4 yearsSimply doing
new Claim(ClaimTypes.NameIdentifier, user.Id)
worked for me. Previously had this asuser.UserName
. Didn't realize the "name identifier" would be an Id. Who woulda thunk. -
poke about 4 years@mwilson How you use the claims mostly depends on your application. There are different ways to approach this, and it also depends on what your id is.
-
eaglei22 about 3 yearsGreat solution. In my case, the Startup.ConfigureServices solution worked for me. Setting a breakpoint in the controller and navigating through the User->claims helped me realize which claim type my organization was using to get the userid. Then setting options.ClaimsIdentity.UserIdClaimType = ClaimTypes.[option] did it for me.
-
ackh over 2 years
UserManager.GetUserAsync
eventually calls the methodFindByIdAsync
of theIUserStore
implementation which is helpful to know if you need to debug a customIUserStore
implementation.