Skip to content

Commit

Permalink
Use System.Text.Json instead of Newtonsoft.Json
Browse files Browse the repository at this point in the history
Package is now 31 KB instead of 542 KB.
  • Loading branch information
gabrielweyer committed Mar 6, 2022
1 parent 6e06201 commit d7ac3b8
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 57 deletions.
26 changes: 18 additions & 8 deletions src/dotnet-decode-jwt/ClaimsDisplayer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.Encodings.Web;

namespace DotNet.Decode.Jwt;

Expand All @@ -13,18 +12,23 @@ public class ClaimsDisplayer
private const string IssuedAtKeyName = "iat";

private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly JsonSerializerOptions SerializationOptions = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};

public ClaimsDisplayer(IConsole console, TimeZoneInfo localTimeZone)
{
_console = console;
_localTimeZone = localTimeZone;
}

public void DisplayClaims(JObject claims)
public void DisplayClaims(JsonElement claims)
{
try
{
if (claims.Count == 0)
if (claims.ValueKind == JsonValueKind.Undefined)
{
_console.ForegroundColor = ConsoleColor.DarkGray;
_console.WriteLine("There was no claims in the JWT.");
Expand All @@ -42,7 +46,7 @@ public void DisplayClaims(JObject claims)
_console.WriteLine(string.Empty);
_console.ResetColor();

_console.WriteLine(JsonConvert.SerializeObject(claims, Formatting.Indented));
_console.WriteLine(JsonSerializer.Serialize(claims, SerializationOptions));
}
}
finally
Expand All @@ -51,11 +55,17 @@ public void DisplayClaims(JObject claims)
}
}

private string FormatDateTime(JObject claims, string key)
private string FormatDateTime(JsonElement claims, string key)
{
if (!claims.TryGetValue(key, out var token)) return "N/A";
if (!claims.TryGetProperty(key, out var token))
{
return "N/A";
}

var timestamp = token.Value<int>();
if (token.ValueKind != JsonValueKind.Number || !token.TryGetInt32(out var timestamp))
{
return "N/A";
}

var utcTime = Epoch.AddSeconds(timestamp);

Expand Down
13 changes: 5 additions & 8 deletions src/dotnet-decode-jwt/JwtClaimsDecoder.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace DotNet.Decode.Jwt;
namespace DotNet.Decode.Jwt;

public static class JwtClaimsDecoder
{
public static JObject GetClaims(string jwt)
public static JsonElement GetClaims(string jwt)
{
var base64UrlClaimsSet = GetBase64UrlClaimsSet(jwt);
var claimsSet = DecodeBase64Url(base64UrlClaimsSet);

try
{
return JObject.Parse(claimsSet);
using var jsonDocument = JsonDocument.Parse(claimsSet);
return jsonDocument.RootElement.Clone();
}
catch (JsonReaderException e)
catch (Exception e)
{
throw new FormatException(e.Message, e);
}
Expand Down
9 changes: 6 additions & 3 deletions src/dotnet-decode-jwt/dotnet-decode-jwt.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<Using Include="System.Text" />
<Using Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -30,6 +33,6 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
69 changes: 61 additions & 8 deletions tests/dotnet-decode-jwt-tests/ClaimsDisplayerTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Globalization;
using System.Runtime.InteropServices;
using FluentAssertions;

namespace DotNet.Decode.Jwt.Tests;

Expand Down Expand Up @@ -43,7 +42,7 @@ static ClaimsDisplayerTests()
public void GivenNoClaim_WhenDisplayClaims_ThenMessage()
{
// Arrange
var claims = new JObject();
var claims = new JsonElement();

// Act
_target.DisplayClaims(claims);
Expand All @@ -63,14 +62,10 @@ public void GivenNoClaim_WhenDisplayClaims_ThenMessage()
public void GivenAnyClaim_WhenDisplayClaims_ThenDisplayClaims()
{
// Arrange
var claims = JObject.Parse(@"
{
'iat': 1516239022
}
");
var claims = JsonDocument.Parse(@"{""iat"":1516239022}");

// Act
_target.DisplayClaims(claims);
_target.DisplayClaims(claims.RootElement);

// Assert
var expected = new List<string>
Expand All @@ -91,6 +86,64 @@ public void GivenAnyClaim_WhenDisplayClaims_ThenDisplayClaims()

_console.Actions.Should().BeEquivalentTo(expected);
}

[Fact]
public void GivenIatIsNotTimestamp_WhenDisplayClaims_ThenDisplayDateAsNotAvailable()
{
// Arrange
var claims = JsonDocument.Parse(@"{""iat"":""hello""}");

// Act
_target.DisplayClaims(claims.RootElement);

// Assert
var expected = new List<string>
{
"WRITE: ",
"SET FOREGROUND COLOR: Yellow",
"WRITE: Expiration Time (exp): N/A",
"WRITE: Not Before (nbf): N/A",
"WRITE: Issued At (iat): N/A",
"SET FOREGROUND COLOR: Green",
"WRITE: ",
"WRITE: Claims are:",
"WRITE: ",
"RESET COLOR",
$"WRITE: {{{Environment.NewLine} \"iat\": \"hello\"{Environment.NewLine}}}",
"RESET COLOR"
};

_console.Actions.Should().BeEquivalentTo(expected);
}

[Fact]
public void GivenHtmlSensitiveCharacter_WhenDisplayClaims_ThenDoNotEscape()
{
// Arrange
var claims = JsonDocument.Parse(@"{""hi"":""I'm""}");

// Act
_target.DisplayClaims(claims.RootElement);

// Assert
var expected = new List<string>
{
"WRITE: ",
"SET FOREGROUND COLOR: Yellow",
"WRITE: Expiration Time (exp): N/A",
"WRITE: Not Before (nbf): N/A",
"WRITE: Issued At (iat): N/A",
"SET FOREGROUND COLOR: Green",
"WRITE: ",
"WRITE: Claims are:",
"WRITE: ",
"RESET COLOR",
$"WRITE: {{{Environment.NewLine} \"hi\": \"I'm\"{Environment.NewLine}}}",
"RESET COLOR"
};

_console.Actions.Should().BeEquivalentTo(expected);
}
}

internal class MockConsole : IConsole
Expand Down
55 changes: 27 additions & 28 deletions tests/dotnet-decode-jwt-tests/JwtClaimsDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,10 @@ public void GivenIatIsNumber_WhenGetClaims_ThenReturnClaims()
var actualClaims = JwtClaimsDecoder.GetClaims(jwt);

// Assert
var expectedClaims = JObject.Parse(@"
{
'sub': '1234567890',
'name': 'John Doe',
'iat': 1516239022
}
");

Assert.Equal(expectedClaims, actualClaims);
var expectedClaims =
JsonDocument.Parse(@"{""sub"":""1234567890"",""name"":""John Doe"",""iat"":1516239022}");

actualClaims.GetRawText().Should().Be(expectedClaims.RootElement.GetRawText());
}

[Fact]
Expand All @@ -69,13 +64,9 @@ public void GivenAudIsArrayOfString_WhenGetClaims_ThenReturnClaims()
var actualClaims = JwtClaimsDecoder.GetClaims(jwt);

// Assert
var expectedClaims = JObject.Parse(@"
{
'aud': ['audience-one','audience-two']
}
");
var expectedClaims = JsonDocument.Parse(@"{""aud"":[""audience-one"",""audience-two""]}");

Assert.Equal(expectedClaims, actualClaims);
actualClaims.GetRawText().Should().Be(expectedClaims.RootElement.GetRawText());
}

[Fact]
Expand All @@ -88,31 +79,39 @@ public void GivenAudIsSingleString_WhenGetClaims_ThenReturnClaims()
var actualClaims = JwtClaimsDecoder.GetClaims(jwt);

// Assert
var expectedClaims = JObject.Parse(@"
{
'aud': 'audience'
}
");
var expectedClaims = JsonDocument.Parse(@"{""aud"":""audience""}");

actualClaims.GetRawText().Should().Be(expectedClaims.RootElement.GetRawText());
}

[Fact]
public void GivenHtmlSensitiveCharacter_WhenGetClaims_ThenReturnUnescapedClaims()
{
// Arrange
const string jwt = "eyJhbGciOiJub25lIn0.eyJoaSI6IkknbSJ9.";

// Act
var actualClaims = JwtClaimsDecoder.GetClaims(jwt);

// Assert
var expectedClaims = JsonDocument.Parse(@"{""hi"":""I'm""}");

Assert.Equal(expectedClaims, actualClaims);
actualClaims.GetRawText().Should().Be(expectedClaims.RootElement.GetRawText());
}

[Fact]
public void GivenClaimKeyIsXmlNamespace_WhenGetClaims_ThenReturnClaims()
{
// Arrange
const string jwt = "eyJhbGciOiJub25lIn0.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiAiaGlAbWUuY29tIn0=.";
const string jwt = "eyJhbGciOiJub25lIn0.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJoaUBtZS5jb20ifQ==.";

// Act
var actualClaims = JwtClaimsDecoder.GetClaims(jwt);

// Assert
var expectedClaims = JObject.Parse(@"
{
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'hi@me.com'
}
");
var expectedClaims = JsonDocument.Parse(
@"{""http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"":""hi@me.com""}");

Assert.Equal(expectedClaims, actualClaims);
actualClaims.GetRawText().Should().Be(expectedClaims.RootElement.GetRawText());
}
}
4 changes: 2 additions & 2 deletions tests/dotnet-decode-jwt-tests/dotnet-decode-jwt-tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Using Include="Newtonsoft.Json.Linq" />
<Using Include="FluentAssertions" />
<Using Include="System.Text.Json" />
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.5.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
Expand Down

0 comments on commit d7ac3b8

Please sign in to comment.