37

I have built an application which uses JWT bearer authentication in ASP.NET Core. When authenticating I define some custom claims which i need to read in another WebAPI controller in order to execute some actions.

Any ideas How Can I achieve this?

This how my code looks like:(Code has been simplified)

public async Task<IActionResult> AuthenticateAsync([FromBody] UserModel user)
    {
        ..............

                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim("userSecurityKey", userDeserialized.SecurityKey.ToString()),
                        new Claim("timeStamp",timeStamp),
                        new Claim("verificationKey",userDeserialized.VerificationKey.ToString())

                    }),
                    Expires = DateTime.UtcNow.AddDays(7),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
                        SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);

     .................                           

    }

Another controller: (It Needs to read "verificationKey" claim.)

    [HttpGet]
    [Route("getcandidate")]
    public async Task<IActionResult> GetCandidateAsync()
    {

        try
        {
             ............    


            var verificationKey = //TODO: GET VerificationKey FROM THE TOKEN

            var verificationRecord = await service.GetVerificationRecordAsync(verificationKey);

            .................

        }
        catch (Exception)
        {
            return NotFound();
        }
    }
D.B
  • 4,009
  • 14
  • 46
  • 83

7 Answers7

91

You should be able to retrieve a claims like this within your controller

var identity = HttpContext.User.Identity as ClaimsIdentity;
if (identity != null)
{
    IEnumerable<Claim> claims = identity.Claims; 
    // or
    identity.FindFirst("ClaimName").Value;

}

If you wanted, you could write extension methods for the IPrincipal interface and retrieve claims using the code above, then retrieve them using (for example)

HttpContext.User.Identity.MethodName();

For completeness of the answer. To Decode the JWT token let's write a method to validate the token and extract the information.

public static ClaimsPrincipal ValidateToken(string jwtToken)
    {
        IdentityModelEventSource.ShowPII = true;

        SecurityToken validatedToken;
        TokenValidationParameters validationParameters = new TokenValidationParameters();

        validationParameters.ValidateLifetime = true;

        validationParameters.ValidAudience = _audience.ToLower();
        validationParameters.ValidIssuer = _issuer.ToLower();
        validationParameters.IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Secret));

        ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out validatedToken);


        return principal;
    }

Now we can validate and extract the Claims by using:

ValidateToken(tokenString)?.FindFirst("ClaimName")?.Value

You should note that the ValidateToken method will return null value if the validation fails.

Adrian
  • 8,271
  • 2
  • 26
  • 43
  • please help, private static bool TryRetrieveToken(HttpRequestMessage request, out string token) { token = null; IEnumerable authzHeaders; if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1) { return false; } var bearerToken = authzHeaders.ElementAt(0); token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken; return true; } in this how to get? – Brijesh Mavani Jun 07 '18 at 14:35
  • @adriani6 so what does the `validationToken` do? I am not sure, but does this procedure actually validate the token? Or just return the claims within it? – thalacker May 24 '19 at 15:51
  • To retrieve claims token must be validated first. See second last line of code. – Adrian May 25 '19 at 09:45
  • How can I get the request inside the validationToken()? Or meaby before in my middleware? I want to check the attributes of the asked controller – David Zamudio Jul 17 '20 at 13:27
  • @DavidZamudio Hi, sorry for the late reply. I think this should be raised as a separate question if you haven't done so already. – Adrian Aug 31 '20 at 15:20
  • Do the inbuilt .net auth mechanism make use of HttpContext.User.Identity? – variable Jan 10 '22 at 14:45
  • And can we do IsInRole on HttpContext.User.Identity? – variable Jan 10 '22 at 14:52
  • @variable Those questions do not relate to the above answer (they should probably be answered in their own topics). The built-in auth mechanism does utilise `HttpContext.User`, yes as for the `IsInRole`, I believe that method belongs to the `User` object - I do tend to build my own role handler though so cannot answer that with certainty. – Adrian Jan 10 '22 at 15:32
14

In any controller from net core 2 that has gone through the authorize with the JwtBearerDefaults scheme, you can use:

 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
 public ActionResult Index()
    {
        var user = User.FindFirst("Name").Value;
        //or if u want the list of claims
        var claims = User.Claims;

        return View();
    }
10
// Cast to ClaimsIdentity.
var identity = HttpContext.User.Identity as ClaimsIdentity;

// Gets list of claims.
IEnumerable<Claim> claim = identity.Claims; 

// Gets name from claims. Generally it's an email address.
var usernameClaim = claim
    .Where(x => x.Type == ClaimTypes.Name)
    .FirstOrDefault();

// Finds user.
var userName = await _userManager
    .FindByNameAsync(usernameClaim.Value);

if (userName == null)
{
    return BadRequest();
}

// The rest of your code goes here...
avg_bloke
  • 194
  • 1
  • 11
6

One way to decode JWT, and get claim is using System.IdentityModel.Tokens

public string getJWTTokenClaim(string token, string claimName)
{
    try
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var securityToken = (JwtSecurityToken)tokenHandler.ReadToken(token);
        var claimValue = securityToken.Claims.FirstOrDefault(c => c.Type == claimName)?.Value;
        return claimValue;
    }
    catch (Exception)
    {
        //TODO: Logger.Error
        return null;
    }
}
2

There are a few JWT implementations for .NET Framework. If you use System.IdentityModel.Tokens.Jwt, when you validate the token you get a System.Security.Claims.ClaimsPrincipal that stores the token's claims in its "Claims" property. So you can get the token's claims as follows:

        string token = // ... read the token
        JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
        TokenValidationParameters validationParameters = ...;
        SecurityToken securityToken;
        IPrincipal principal;
        try
        {
            // token validation
            principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
            // Reading the "verificationKey" claim value:
            var vk = principal.Claims.SingleOrDefault(c => c.Type == "verificationKey").Value; 
        }
        catch
        {
            principal = null; // token validation error
        }

Now where do you place this code? My choice was to implement the token validation as an authorization filter attribute derived from AuthorizationFilterAttribute. If you decorate a controller with the attribute, its OnAuthorization method is executed before every call to the controller's endpoints. You place the above code on the OnAuthorization method, and store the principal returned by the token validation on HttpContext.Current.User, that is also accessible on any endpoint on your API. http://blogs.quovantis.com/json-web-token-jwt-with-web-api/ is a nice sample of this implementation.

GBU
  • 184
  • 6
1

Actually you can get any claims like this and then rewrite it on your header to use it anywhere easily

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {

        var s = context.HttpContext.Request.Headers["Authorization"];
        if (AuthenticationHeaderValue.TryParse(s, out var headerValue))
        {
            // we have a valid AuthenticationHeaderValue that has the following details:

            var scheme = headerValue.Scheme;
            var parameter = headerValue.Parameter;

            // scheme will be "Bearer"
            // parmameter will be the token itself.
            // or
            var stream = parameter;
            var handler = new JwtSecurityTokenHandler();
            var jsonToken = handler.ReadToken(stream);
            var tokenS = handler.ReadToken(stream) as JwtSecurityToken;
            context.HttpContext.Request.Headers["UserName"] = tokenS.Claims.FirstOrDefault(a=>a.Type == "UserName")?.Value;


        }

        var user = context.HttpContext.Request.Headers["Authorization"];
        if (user == string.Empty)
        {
            // not logged in
            context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
        }
    }


}
Mamadmti
  • 61
  • 6
0

The straightforward way not found anywhere here is "FindFirstValue". I will most likely write a bunch of tutorials/blog posts on ASP.NET core once I'm done my current project. Modify the following method. I use this in my authorization controller for quick testing.

#if DEBUG
    [HttpGet]
    [Authorize(Roles = "User")]
    public string TestAuthorization() {
        var email = User.FindFirstValue(JwtRegisteredClaimNames.Email);
        return $"Your email is {email}";
    }
#endif

and my GenerateToken method

    private string GenerateToken(string email) {
        var claims = new[] { new Claim(JwtRegisteredClaimNames.Email, email), new Claim(ClaimTypes.Role, "User") };
        var token = new JwtSecurityToken(jwtIssuer, jwtAudience, claims, signingCredentials: credentials);
        return jwtSecurityTokenHandler.WriteToken(token);
    }
Elijah
  • 1,814
  • 21
  • 27