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

南笙酒味 提交于 2019-12-20 09:28:16

问题


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.


回答1:


You can use this method:

var email = User.FindFirst("sub")?.Value;

In my case I'm using the email as a unique value




回答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.




回答3:


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 download the ID of the current user by:

var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;



回答4:


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.




回答5:


I used the HttpContext and it works well:

var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
    email = identity.FindFirst(ClaimTypes.Name).Value;
}



回答6:


you can do this using.

User.Identity.Name



来源:https://stackoverflow.com/questions/46112258/how-do-i-get-current-user-in-net-core-web-api-from-jwt-token

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!