DEV Community

loading...

Setting up JWT and Identity Authorization/Authentication in ASP .NET Core

Alexandru Bucur
A jack of all trades is a master of none, but oftentimes better than a master of one.
・3 min read

Continuing my foray into ASP .NET Core, and making sure I get outside my comfort zone, I got into the situation that I want to be able to easily access the logged in user information in my API request.

There are several versions available online, some unfortunately out of date (mostly because ASP .NET Core 2.0 is relatively new and things changed significantly in Identity between 1.0 and 2.0).

First version I stumbled upon recommended extending the IHttpContextAccessor. In case you don't know, in C# you can use a custom extension method to implement your own extensions to core libraries.

The code in my nativity looked like:

public static class IHttpContextAccessorExtension
{
    public static string CurrentUser(
        this IHttpContextAccessor httpContextAccessor
    ) {
        return httpContextAccessor.HttpContext.User.FindFirst(JwtRegisteredClaimNames.Sub).Value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Technically, that returns the user.Id since that's what my Claim definition specified. (ignore the fact that I only have one of the 'important' tokens)


var claims = new[] {
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    new Claim(JwtRegisteredClaimNames.Sub, user.Id),
};
Enter fullscreen mode Exit fullscreen mode

Because of the fact that Microsoft 'knows' best (yes all separate links, I promise I'll make a docs pull request) we need to add System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); in our Startup.cs / ConfigureServices (easiest is to just dump it at the top) in order to avoid the automatic conversion of the sub type to ClaimTypes.NameIdentifier.

For some reason this is not improved in 2.0 even if it was a good occasion since it broke backwards compatibility (hoping to have this in 2.1 since the recent blog post suggested that 2.1 will not follow SemVer and could introduce breaking changes).

That being changed you can then redefine the UserIdClaimType to use the Jwt claim.

services.AddIdentity<User, IdentityRole>(options =>
{
    ...
    options.ClaimsIdentity.UserIdClaimType = JwtRegisteredClaimNames.Sub;
})
Enter fullscreen mode Exit fullscreen mode

To be honest, I understand why this got ignored, because most of the time, your CurrentUser method would most likely look like:

public static class IHttpContextAccessorExtension
{
    public static async Task<User> CurrentUser(
        this IHttpContextAccessor httpContextAccessor,
        UsersService users
        )
    {
        return await users.UserManager.GetUserAsync(httpContextAccessor.HttpContext.User);
    }
}
Enter fullscreen mode Exit fullscreen mode

This works even if you don't reset the Claim handling since behind the scenes it does the transformation for you into ClaimTypes.NameIdentifier. I'm not a big fan since even if convention over configuration is nice to have, this is a bit too magical for me since it also does a claim type conversion.

Now, as an alternative you could always just call the GetUserAsync method on your UserManager directly inside the controller because you have direct access to HttpContext.User (or this.User, again I like it a bit more explicit).

public async Task<IActionResult> Index()
{
    // _users is my injected service that contains the UserManager
    var currentUser = _users.UserManager.GetUserAsync(HttpContext.User);
}
Enter fullscreen mode Exit fullscreen mode

How are you guys handling this ? Anything that I should keep in mind as best practice ?

Discussion (2)

Collapse
tutlane profile image
Tutlane

Thanks Alexandru. It solve my problem.

Regards
Tutlane.com

Collapse
haidarz profile image
Haidar Zeineddine

The solution above didn't solve my problem ,I followed the given accepted answer
stackoverflow.com/questions/511199...