171

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,705
  • 6
  • 48
  • 63
Cooxkie
  • 5,480
  • 6
  • 20
  • 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
  • You better do not share tokens on public webs, because we can get information that maybe is not secure but sencible – Ernesto Alfonso Mar 09 '22 at 14:52

7 Answers7

285

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;

Or, without the cast:

var token = "[encoded jwt]";  
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);

I can get Claims using:

var jti = tokenS.Claims.First(claim => claim.Type == "jti").Value;
JohnB
  • 16,745
  • 16
  • 96
  • 110
Cooxkie
  • 5,480
  • 6
  • 20
  • 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
  • 7
    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
  • 5
    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
  • 1
    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
  • 1
    There are probably other ways to get the access token in other scenarios, but from within a Controller, I can get it using ---> var token = await HttpContext.GetTokenAsync("access_token"); – mathkid91 Aug 02 '21 at 12:10
54

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
  • 2,396
  • 2
  • 15
  • 23
34

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,714
  • 2
  • 27
  • 36
Pato Milán
  • 438
  • 5
  • 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
17

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
  • 17,272
  • 9
  • 83
  • 149
  • 1
    This is only feasible when using ASP.NET Core based middleware pipeline, OP asked specifically for the handler's implementation inside implementation `System.IdentityModel.Tokens.Jwt` which suits a broader set of use cases. – Beltway Nov 02 '21 at 13:12
16
  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,445
  • 4
  • 35
  • 50
Jinesh
  • 1,350
  • 2
  • 22
  • 49
7

I write this solution and it's work for me

    protected Dictionary<string, string> GetTokenInfo(string token)
    {
        var TokenInfo = new Dictionary<string, string>();

        var handler = new JwtSecurityTokenHandler();
        var jwtSecurityToken = handler.ReadJwtToken(token);
        var claims = jwtSecurityToken.Claims.ToList();

        foreach (var claim in claims)
        {
            TokenInfo.Add(claim.Type, claim.Value);
        }

        return TokenInfo;
    }
Adam
  • 133
  • 2
  • 6
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