131

I don't understand how this library works. Could you help me please ?

Here is my simple code :

public void TestJwtSecurityTokenHandler()
    {
        var stream =
            "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJJU1MiLCJzY29wZSI6Imh0dHBzOi8vbGFyaW0uZG5zY2UuZG91YW5lL2NpZWxzZXJ2aWNlL3dzIiwiYXVkIjoiaHR0cHM6Ly9kb3VhbmUuZmluYW5jZXMuZ291di5mci9vYXV0aDIvdjEiLCJpYXQiOiJcL0RhdGUoMTQ2ODM2MjU5Mzc4NClcLyJ9";
        var handler = new JwtSecurityTokenHandler();

        var jsonToken = handler.ReadToken(stream);
    }

This is the error :

The string needs to be in compact JSON format, which is of the form: Base64UrlEncodedHeader.Base64UrlEndcodedPayload.OPTIONAL,Base64UrlEncodedSignature'.

If you copy the stream in jwt.io website, it works fine :)

Matze
  • 4,433
  • 4
  • 45
  • 59
Cooxkie
  • 4,470
  • 6
  • 19
  • 25
  • 1
    the jwt,io site decodes it, but there is no signature so it is invalid. – Crowcoder Jul 13 '16 at 01:53
  • Possible duplicate of [Decoding and verifying JWT token using System.IdentityModel.Tokens.Jwt](https://stackoverflow.com/questions/18677837/decoding-and-verifying-jwt-token-using-system-identitymodel-tokens-jwt) – Michael Freidgeim Sep 15 '17 at 11:32
  • 1
    @MichaelFreidgeim you're right it's duplicate question ... but answers are different because of version library you use – Cooxkie Sep 15 '17 at 12:05

6 Answers6

225

I found the solution, I just forgot to Cast the result:

var stream ="[encoded jwt]";  
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
var tokenS = jsonToken as JwtSecurityToken;

I can get Claims using:

var jti = tokenS.Claims.First(claim => claim.Type == "jti").Value;
OpMt
  • 1,504
  • 4
  • 17
  • 24
Cooxkie
  • 4,470
  • 6
  • 19
  • 25
  • 2
    I had to cast tokenS.Claims as a List of Claims first. `((List)tokenS.Claims).ForEach(a => Console.WriteLine(a.Type.ToString() + " " + a.Value));` – Rinaldi Segecin Apr 13 '17 at 22:44
  • For Registered Claims you can get the property value directly off of the `JwtSecurityToken`. For example, `tokenS.Id` will get you the jti value – user875318 Jan 30 '18 at 14:30
  • 13
    You can also do: handler.ReadJwtToken(tokenJwtReponse.access_token); – Thabiso Mofokeng Feb 20 '18 at 15:30
  • Also, don't use type-safe casting unless you are expecting it to NOT be of that type. Use `(JwtSecurityToken)handler.ReadToken(tokenJwtReponse.access_token)` so that you get `InvalidCastException` instead of some later `NullReferenceException`. See [this](https://codeblog.jonskeet.uk/2013/09/19/casting-vs-quot-as-quot-embracing-exceptions/) – oatsoda Jun 04 '18 at 08:42
  • 15
    Sorry if this should be obvious but where is `tokenJwtReponse.access_token` coming from? – Jeff Stapleton Mar 20 '19 at 04:02
  • 4
    Where is tokenJwtReponse.access_token coming from? – 3iL Mar 20 '19 at 12:34
  • 5
    As others have already questioned: where does "tokenJwtReponse.access_token" come from? There is no definition or declaration for it in the answer, making the answer useless and meaningless for many of us. – Zeek2 Apr 08 '19 at 15:38
  • I'm assuming `tokenJwtResponse` is a deserialized json response containing a key `access_token` with the actual JWT token. – Niklas Holm Apr 25 '19 at 09:14
  • replace tokenJwtReponse.access_token in the above with stream – Rob McFeely Jul 03 '19 at 13:16
  • Your examples should be self contained and testable. This doesn't build. – Timothy Gonzalez Nov 05 '19 at 04:45
  • 2
    I needed to include the following NuGet package to use it within my Azure Function: System.IdentityModel.Tokens.Jwt – David Yates Jan 16 '20 at 02:54
  • the tokenJwtReponse.access_token is simply the string of the access token (without "Bearer " in the start) – Yonatan Nir Feb 15 '21 at 09:33
46

new JwtSecurityTokenHandler().ReadToken("") will return a SecurityToken

new JwtSecurityTokenHandler().ReadJwtToken("") will return a JwtSecurityToken

If you just change the method you are using you can avoid the cast in the above answer

dpix
  • 1,936
  • 1
  • 12
  • 22
23

You need the secret string which was used to generate encrypt token. This code works for me:

protected string GetName(string token)
    {
        string secret = "this is a string used for encrypt and decrypt token"; 
        var key = Encoding.ASCII.GetBytes(secret);
        var handler = new JwtSecurityTokenHandler();
        var validations = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false
        };
        var claims = handler.ValidateToken(token, validations, out var tokenSecure);
        return claims.Identity.Name;
    }
jaycer
  • 2,460
  • 2
  • 22
  • 36
Pato Milán
  • 329
  • 3
  • 5
  • Why do you call `handler.ReadToken(token) as SecurityToken` when you're reassigning it as your `out` parameter later? Is there a possibility that `ValidateToken` fails and the original value is kept? – krillgar Feb 27 '19 at 15:49
  • Right krillgar is not nesessary the cast to SecurityToken – Pato Milán Apr 04 '19 at 21:22
  • Does ValidateToken check the expiration? Or do I need to validate that myself after it is decoded? – computrius Jun 03 '20 at 15:46
  • @computrius ValidateToken takes a `TokenValidationParameters`, which is constructed on the line before the call, as clearly seen in the answer. This object will tell the validator what to check for. – Zimano Apr 23 '21 at 09:23
12

Using .net core jwt packages, the Claims are available:

[Route("api/[controller]")]
[ApiController]
[Authorize(Policy = "Bearer")]
public class AbstractController: ControllerBase
{
    protected string UserId()
    {
        var principal = HttpContext.User;
        if (principal?.Claims != null)
        {
            foreach (var claim in principal.Claims)
            {
               log.Debug($"CLAIM TYPE: {claim.Type}; CLAIM VALUE: {claim.Value}");
            }

        }
        return principal?.Claims?.SingleOrDefault(p => p.Type == "username")?.Value;
    }
}
jenson-button-event
  • 15,947
  • 8
  • 73
  • 144
10
  var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
  var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
  var claims = new[]
  {
      new Claim(JwtRegisteredClaimNames.Email, model.UserName),
      new Claim(JwtRegisteredClaimNames.NameId, model.Id.ToString()),
  };
  var token = new JwtSecurityToken(_config["Jwt:Issuer"],
      _config["Jwt:Issuer"],
      claims,
      expires: DateTime.Now.AddMinutes(30),
      signingCredentials: creds);

Then extract content

 var handler = new JwtSecurityTokenHandler();
 string authHeader = Request.Headers["Authorization"];
 authHeader = authHeader.Replace("Bearer ", "");
 var jsonToken = handler.ReadToken(authHeader);
 var tokenS = handler.ReadToken(authHeader) as JwtSecurityToken;
 var id = tokenS.Claims.First(claim => claim.Type == "nameid").Value;
SolarBear
  • 4,282
  • 4
  • 33
  • 50
Jinesh
  • 1,230
  • 1
  • 21
  • 44
3

Extending on cooxkie answer, and dpix answer, when you are reading a jwt token (such as an access_token received from AD FS), you can merge the claims in the jwt token with the claims from "context.AuthenticationTicket.Identity" that might not have the same set of claims as the jwt token.

To Illustrate, in an Authentication Code flow using OpenID Connect,after a user is authenticated, you can handle the event SecurityTokenValidated which provides you with an authentication context, then you can use it to read the access_token as a jwt token, then you can "merge" tokens that are in the access_token with the standard list of claims received as part of the user identity:

    private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage,OpenIdConnectAuthenticationOptions> context)
    {
        //get the current user identity
        ClaimsIdentity claimsIdentity = (ClaimsIdentity)context.AuthenticationTicket.Identity;

        /*read access token from the current context*/
        string access_token = context.ProtocolMessage.AccessToken;

        JwtSecurityTokenHandler hand = new JwtSecurityTokenHandler();
        //read the token as recommended by Coxkie and dpix
        var tokenS = hand.ReadJwtToken(access_token);
        //here, you read the claims from the access token which might have 
        //additional claims needed by your application
        foreach (var claim in tokenS.Claims)
        {
            if (!claimsIdentity.HasClaim(claim.Type, claim.Value))
                claimsIdentity.AddClaim(claim);
        }

        return Task.FromResult(0);
    }
TamerDev
  • 81
  • 3