How do I get current user in .NET Core Web API (from JWT Token)

82,275

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

Share:
82,275
monty
Author by

monty

Updated on July 05, 2022

Comments

  • monty
    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
    heringer over 6 years
    Thanks, 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
    monty over 6 years
    another way could be: string sub = HttpContext?.User.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;
  • Gustavo Piucco
    Gustavo Piucco over 5 years
    Or User.FindFirstValue(ClaimTypes.Name)
  • aruno
    aruno over 5 years
    Note that if you aren't using [Authorize] attributes, that User may be a sort of empty user where User.Identity.IsAuthenticated is false. So watch out for that.
  • jezpez
    jezpez about 5 years
    Having 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
    Seafish almost 5 years
  • Honza Kalfus
    Honza Kalfus over 4 years
    @Seafish Be sure to make a null check in that case as well.
  • farshad
    farshad about 4 years
    I 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
    troYman almost 4 years
    var userId = User.Identity.Name
  • Ali.Rashidi
    Ali.Rashidi over 2 years
    User does not exist in the current context. how can i access User?
  • variable
    variable over 2 years
    How did you get the user ? User. what is user here?
  • Honza Kalfus
    Honza Kalfus over 2 years
    User is an instance of ClaimsPrincipal. Typically, you can retrieve it from the HttpContext, see docs.microsoft.com/en-us/dotnet/api/…
  • Telmo Marques
    Telmo Marques about 2 years
    @Seafish that's a 404 now.