How do I get current user in .NET Core Web API (from JWT Token)
Solution 1
It seems a lot of people are looking at this question so I would like to share some more information I learned since I asked the question a while back. It makes some things more clear (at least for me) and wasn't so obvious (for me as .NET newbie).
As Marcus Höglund mentioned in the comments:
It should be the same for "web api"..In ASP.NET Core Mvc and Web Api are merged to use the same controller.
That's definitely true and absolutely correct.
Because it is all the same across .NET and .NET Core.
Back than I was new to .NET Core and actually the full .NET world. The important missing information was that in .NET and .NET Core all the authentication can be trimmed down to System.Security.Claims namespace with its ClaimsIdentity, ClaimsPrinciple and Claims.Properties. And therefore it is used in both .NET Core controller types (API and MVC or Razor or ...) and is accessible via HttpContext.User
.
An important side note all of the tutorials missed to tell.
So if you start doing something with JWT tokens in .NET don't forget to also get confident with ClaimsIdentity, ClaimsPrinciple and Claim.Properties. It's all about that. Now you know it. It was pointed out by Heringer in one of the comments.
ALL the claim based authentication middlewares will (if correctly implemented) populate the HttpContext.User
with the claims received during authentication.
As far as I understand now this means one can safely trust on the values in the HttpContext.User
. But wait a bit to know what to mind when selecting middleware. There are a lot of different authentication
middleware already available (in addition to .UseJwtAuthentication()
).
With small custom extension methods you can now get the current user id (more accurate the subject claim) like that
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }
Or you use the version in the answer of Ateik.
BUT WAIT: there is one strange thing
The next thing that confused me back than: according to the OpenID Connect spec I was looking for "sub" claim (the current user) but couldn't find it. Like Honza Kalfus couldn't do in his answer.
Why?
Because Microsoft is "sometimes" "a bit" different. Or at least they do a bit more (and unexpected) things. For example the official Microsoft JWT Bearer authentication middleware mentioned in the original question. Microsoft decided to convert claims (the names of the claims) in all of their official authentication middleware (for compatibility reasons I don't know in more detail).
You won't find a "sub" claim (though it is the single one claim specified by OpenID Connect). Because it got converted to these fancy ClaimTypes. It's not all bad, it allows you to add mappings if you need to map different claims into a unique internal name.
Either you stick with the Microsoft naming (and have to mind that when you add/use a non Microsoft middleware) or you find out how to turn the claim mapping of for the Microsoft middleware.
In case of the JwtBearerAuthentication it is done (do it early in StartUp or at least before adding the middleware):
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
If you want to stick with the Microsoft namings the subject claim (don't beat me, I am not sure right now if Name is the correct mapping):
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase))?.Value; }
Note that the other answers use the more advanced and way more convenient FindFirst method. Though my code samples show it without those you may should go with them.
So all your claims are stored and accessible (via one name or the other) in the HttpContext.User
.
But where is my token?
I don't know for the other middleware but the JWT Bearer Authentication allows to save the token for each request. But this needs to be activated (in StartUp.ConfigureServices(...
).
services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => options.SaveToken = true);
The actual token (in all it's cryptic form) as string (or null) can then be accessed via
HttpContext.GetTokenAsync("Bearer", "access_token")
There has been an older version of this method (this works for me in .NET Core 2.2 without deprecated warning).
If you need to parse and extract values from this string may the question How to decode JWT token helps.
Well, I hope that summary helps you too.
Solution 2
The accepted answer did not work for me. I'm not sure if that's caused by me using .NET Core 2.0 or by something else, but it looks like the framework maps the Subject Claim to a NameIdentifier claim. So, the following worked for me:
string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
Note that this assumes the Subject sub
Claim is set in the JWT and its value is the user's id.
By default, the JWT authentication handler in .NET will map the sub claim of a JWT access token to the System.Security.Claims.ClaimTypes.NameIdentifier
claim type. [Source]
There is also a discussion thread on GitHub where they conclude this behavior is confusing.
Solution 3
You can use this method:
var email = User.FindFirst("sub")?.Value;
In my case I'm using the email as a unique value
Solution 4
If you use Name
to store the ID
here:
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
In each controller method you can get the ID of the current user by:
var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;
Solution 5
you can do this using.
User.Identity.Name
monty
Updated on July 05, 2022Comments
-
monty almost 2 years
After a lot of struggling (and a lot of tuturials, guides, etc) I managed to setup a small .NET Core REST Web API with an Auth Controller issuing JWT tokens when stored username and password are valid.
The token stores the user id as sub claim.
I also managed to setup the Web API to validate those tokens when a method uses the Authorize annotation.
app.UseJwtBearerAuthentication(...)
Now my question: How do I read the user id (stored in the subject claim) in my controllers (in a Web API)?
It is basically this question (How do I get current user in ASP .NET Core) but I need an answer for a web api. And I do not have a UserManager. So I need to read the subject claim from somewhere.
-
heringer over 6 yearsThanks, should be marked as accepted answer! For the user name: User.Identity.Name. User is a property of Microsoft.AspNetCore.Mvc.ControlerBase and it's type is System.Security.Claims.ClaimsPrincipal. Just adding.
-
monty over 6 yearsanother way could be: string sub = HttpContext?.User.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;
-
Gustavo Piucco over 5 yearsOr User.FindFirstValue(ClaimTypes.Name)
-
aruno over 5 yearsNote that if you aren't using
[Authorize]
attributes, thatUser
may be a sort of empty user whereUser.Identity.IsAuthenticated
is false. So watch out for that. -
jezpez about 5 yearsHaving the claims in the actual token match up exactly to what is in available in the controller makes so much sense. I think your answer should begin with this in Startup.cs: JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); Then this in the Controller.cs: var userSub = User.FindFirst("sub")?.Value; This was the only answer which fully explained the problem without just linking to a confusing thread on GitHub. Well done.
-
Seafish almost 5 yearsOr slightly shorter:
User.FindFirstValue(ClaimTypes.NameIdentifier)
-
Honza Kalfus over 4 years@Seafish Be sure to make a null check in that case as well.
-
farshad about 4 yearsI wish I had read this answer earlier. Wasted an hour to find out how .Net changed the claim type. I explicitly named the claim "sub" and I just I couldn't figure out why "jti" is working but not the "sub". This can get many people new to JWT in .Net Core confused. Your answer clarifies it.
-
troYman almost 4 yearsvar userId = User.Identity.Name
-
Ali.Rashidi over 2 yearsUser does not exist in the current context. how can i access User?
-
variable over 2 yearsHow did you get the user ? User. what is user here?
-
Honza Kalfus over 2 yearsUser is an instance of
ClaimsPrincipal
. Typically, you can retrieve it from the HttpContext, see docs.microsoft.com/en-us/dotnet/api/… -
Telmo Marques about 2 years@Seafish that's a 404 now.