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 controller
Claim claimUserId = User.Claims.SingleOrDefault(c => c.Type == "UserID");
The ControllerBase.User
instance holds .Identity
object which in turn holds Claims
collection.
Identity.IsAuthenticated
equals True
.
Identity.Name
holds admin
string (name of the relevant user).
If I try to fetch user like this:
var user = await UserManager.GetUserAsync(HttpContext.User)
the user
is null
.
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 by claim
which holds UserID
?
Or maybe there's a better approach?
Additional info:
The Identity
is registered as follows
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
ApplicationUser.Id
field is of bigint
(or in C# of long
) type
Also, I create users in EF Seed Data
with UserManager which is resolved using ServiceProvider
_userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
...
adminUser.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(adminUser, "123qwe");
_userManager.CreateAsync(adminUser);
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";
});
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)
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With