Skip to content

Commit 068140b

Browse files
committed
Update to newest JsonWebTokens
Replace older (and effectively legacy) Jwt package and adopt the new (and faster) Identity package recommended by Microsoft.
1 parent 81ba912 commit 068140b

8 files changed

Lines changed: 176 additions & 119 deletions

File tree

src/SponsorLink/SponsorLink.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@
7171
<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' == 'true'">
7272
<PackageReference Include="Humanizer.Core" VersionOverride="2.14.1" PrivateAssets="all" Pack="false" />
7373
<PackageReference Include="Humanizer.Core.es" VersionOverride="2.14.1" PrivateAssets="all" />
74-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" VersionOverride="7.6.0" PrivateAssets="all" Pack="false" />
74+
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" VersionOverride="7.6.2" PrivateAssets="all" Pack="false" />
7575
<PackageReference Include="ILRepack" Version="2.0.33" VersionOverride="all" PrivateAssets="all" Pack="false" />
7676
</ItemGroup>
7777

7878
<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' != 'true'">
7979
<PackageReference Include="Humanizer.Core" Version="2.14.1" PrivateAssets="all" Pack="false" />
8080
<PackageReference Include="Humanizer.Core.es" Version="2.14.1" PrivateAssets="all" />
81-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" PrivateAssets="all" Pack="false" />
81+
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" PrivateAssets="all" Pack="false" />
8282
<PackageReference Include="ILRepack" Version="2.0.33" PrivateAssets="all" Pack="false" />
8383
</ItemGroup>
8484

src/SponsorLink/SponsorLink/SponsorLink.cs

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.IdentityModel.Tokens.Jwt;
76
using System.Linq;
87
using System.Reflection;
98
using System.Security.Claims;
9+
using Microsoft.IdentityModel.JsonWebTokens;
1010
using Microsoft.IdentityModel.Tokens;
1111

1212
namespace Devlooped.Sponsors;
@@ -85,12 +85,12 @@ public static bool TryRead([NotNullWhen(true)] out ClaimsPrincipal? principal, I
8585
if (string.IsNullOrWhiteSpace(value.jwt) || string.IsNullOrEmpty(value.jwk))
8686
continue;
8787

88-
if (Validate(value.jwt, value.jwk, out var token, out var claims, false) == ManifestStatus.Valid && claims != null)
88+
if (Validate(value.jwt, value.jwk, out var token, out var identity, false) == ManifestStatus.Valid && identity != null)
8989
{
9090
if (principal == null)
91-
principal = claims;
91+
principal = new(identity);
9292
else
93-
principal.AddIdentities(claims.Identities);
93+
principal.AddIdentity(identity);
9494
}
9595
}
9696

@@ -103,13 +103,13 @@ public static bool TryRead([NotNullWhen(true)] out ClaimsPrincipal? principal, I
103103
/// <param name="jwt">The JWT to validate.</param>
104104
/// <param name="jwk">The key to validate the manifest signature with.</param>
105105
/// <param name="token">Except when returning <see cref="Status.Unknown"/>, returns the security token read from the JWT, even if signature check failed.</param>
106-
/// <param name="principal">The associated claims, only when return value is not <see cref="Status.Unknown"/>.</param>
106+
/// <param name="identity">The associated claims, only when return value is not <see cref="Status.Unknown"/>.</param>
107107
/// <param name="requireExpiration">Whether to check for expiration.</param>
108108
/// <returns>The status of the validation.</returns>
109-
public static ManifestStatus Validate(string jwt, string jwk, out SecurityToken? token, out ClaimsPrincipal? principal, bool validateExpiration)
109+
public static ManifestStatus Validate(string jwt, string jwk, out SecurityToken? token, out ClaimsIdentity? identity, bool validateExpiration)
110110
{
111111
token = default;
112-
principal = default;
112+
identity = default;
113113

114114
SecurityKey key;
115115
try
@@ -121,7 +121,7 @@ public static ManifestStatus Validate(string jwt, string jwk, out SecurityToken?
121121
return ManifestStatus.Unknown;
122122
}
123123

124-
var handler = new JwtSecurityTokenHandler { MapInboundClaims = false };
124+
var handler = new JsonWebTokenHandler { MapInboundClaims = false };
125125

126126
if (!handler.CanReadToken(jwt))
127127
return ManifestStatus.Unknown;
@@ -138,32 +138,35 @@ public static ManifestStatus Validate(string jwt, string jwk, out SecurityToken?
138138
NameClaimType = "sub",
139139
};
140140

141-
try
141+
var result = handler.ValidateTokenAsync(jwt, validation).Result;
142+
if (result.Exception != null)
142143
{
143-
principal = handler.ValidateToken(jwt, validation, out token);
144-
if (validateExpiration && token.ValidTo == DateTime.MinValue)
144+
if (result.Exception is SecurityTokenInvalidSignatureException)
145+
{
146+
var jwtToken = handler.ReadJsonWebToken(jwt);
147+
token = jwtToken;
148+
identity = new ClaimsIdentity(jwtToken.Claims);
149+
return ManifestStatus.Invalid;
150+
}
151+
else
152+
{
153+
var jwtToken = handler.ReadJsonWebToken(jwt);
154+
token = jwtToken;
155+
identity = new ClaimsIdentity(jwtToken.Claims);
145156
return ManifestStatus.Invalid;
157+
}
158+
}
146159

147-
// The sponsorable manifest does not have an expiration time.
148-
if (validateExpiration && token.ValidTo < DateTimeOffset.UtcNow)
149-
return ManifestStatus.Expired;
160+
token = result.SecurityToken;
161+
identity = new ClaimsIdentity(result.ClaimsIdentity.Claims);
150162

151-
return ManifestStatus.Valid;
152-
}
153-
catch (SecurityTokenInvalidSignatureException)
154-
{
155-
var jwtToken = handler.ReadJwtToken(jwt);
156-
token = jwtToken;
157-
principal = new ClaimsPrincipal(new ClaimsIdentity(jwtToken.Claims));
158-
return ManifestStatus.Invalid;
159-
}
160-
catch (SecurityTokenException)
161-
{
162-
var jwtToken = handler.ReadJwtToken(jwt);
163-
token = jwtToken;
164-
principal = new ClaimsPrincipal(new ClaimsIdentity(jwtToken.Claims));
163+
if (validateExpiration && token.ValidTo == DateTime.MinValue)
165164
return ManifestStatus.Invalid;
166-
}
167-
}
168165

166+
// The sponsorable manifest does not have an expiration time.
167+
if (validateExpiration && token.ValidTo < DateTimeOffset.UtcNow)
168+
return ManifestStatus.Expired;
169+
170+
return ManifestStatus.Valid;
171+
}
169172
}

src/SponsorLink/SponsorLink/SponsorLink.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<PackageReference Include="NuGetizer" Version="1.2.2" />
2525
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" Pack="false" />
2626
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" />
27-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" PrivateAssets="all" />
27+
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" PrivateAssets="all" />
2828
</ItemGroup>
2929

3030
<ItemGroup>

src/SponsorLink/Tests/.netconfig

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
[config]
2+
root = true
13
[file "SponsorableManifest.cs"]
24
url = https://github.com/devlooped/SponsorLink/blob/main/src/Core/SponsorableManifest.cs
3-
sha = 976ecefc44d87217e04933d9cd7f6b950468410b
4-
etag = e0c95e7fc6c0499dbc8c5cd28aa9a6a5a49c9d0ad41fe028a5a085aca7e00eaf
5+
sha = 5a4cad3a084f53afe34a6b75e4f3a084a0f1bf9e
6+
etag = 9a07c856d06e0cde629fce3ec014f64f9adfd5ae5805a35acf623eba0ee045c1
57
weak
68
[file "JsonOptions.cs"]
79
url = https://github.com/devlooped/SponsorLink/blob/main/src/Core/JsonOptions.cs
8-
sha = 79dc56ce45fc36df49e1c4f8875e93c297edc383
9-
etag = 6e9a1b12757a97491441b9534ced4e5dac6d9d6334008fa0cd20575650bbd935
10+
sha = 80ea1bfe47049ef6c6ed4f424dcf7febb729cbba
11+
etag = 17799725ad9b24eb5998365962c30b9a487bddadca37c616e35b76b8c9eb161a
1012
weak
1113
[file "Extensions.cs"]
1214
url = https://github.com/devlooped/SponsorLink/blob/main/src/Core/Extensions.cs
13-
sha = d204b667eace818934c49e09b5b08ea82aef87fa
14-
etag = f68e11894103f8748ce290c29927bf1e4f749e743ae33d5350e72ed22c15d245
15-
weak
15+
sha = c455f6fa1a4d404181d076d7f3362345c8ed7df2
16+
etag = 9e51b7e6540fae140490a5283b1e67ce071bd18a267bc2ae0b35c7248261aed1
17+
weak

src/SponsorLink/Tests/Extensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Diagnostics.CodeAnalysis;
22
using System.Runtime.CompilerServices;
3+
using System.Security.Cryptography;
34
using Microsoft.Extensions.Logging;
5+
using Microsoft.IdentityModel.Tokens;
46

57
namespace Devlooped.Sponsors;
68

@@ -23,6 +25,17 @@ public static HashCode AddRange<T>(this HashCode hash, IEnumerable<T> items)
2325
return hash;
2426
}
2527

28+
public static bool ThumbprintEquals(this SecurityKey key, RSA rsa) => key.ThumbprintEquals(new RsaSecurityKey(rsa));
29+
30+
public static bool ThumbprintEquals(this RSA rsa, SecurityKey key) => key.ThumbprintEquals(rsa);
31+
32+
public static bool ThumbprintEquals(this SecurityKey first, SecurityKey second)
33+
{
34+
var expectedKey = JsonWebKeyConverter.ConvertFromSecurityKey(second);
35+
var actualKey = JsonWebKeyConverter.ConvertFromSecurityKey(first);
36+
return expectedKey.ComputeJwkThumbprint().AsSpan().SequenceEqual(actualKey.ComputeJwkThumbprint());
37+
}
38+
2639
public static Array Cast(this Array array, Type elementType)
2740
{
2841
//Convert the object list to the destination array type.

src/SponsorLink/Tests/JsonOptions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Globalization;
1+
using System.Globalization;
42
using System.Text.Json;
53
using System.Text.Json.Serialization;
64
using System.Text.Json.Serialization.Metadata;

0 commit comments

Comments
 (0)