diff --git a/Directory.Packages.props b/Directory.Packages.props
index 04e0a111..4d523006 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,7 +14,7 @@
-
+
diff --git a/src/D2L.Security.OAuth2/D2L.Security.OAuth2.csproj b/src/D2L.Security.OAuth2/D2L.Security.OAuth2.csproj
index 914a770d..fd36da48 100644
--- a/src/D2L.Security.OAuth2/D2L.Security.OAuth2.csproj
+++ b/src/D2L.Security.OAuth2/D2L.Security.OAuth2.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs b/src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs
index 8bb0fd64..b837870d 100644
--- a/src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs
+++ b/src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs
@@ -1,14 +1,15 @@
-using System.IdentityModel.Tokens.Jwt;
-using System.Linq;
-using System.Security.Claims;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Validation.Exceptions;
+using Microsoft.IdentityModel.Tokens;
+using System.Collections.Generic;
namespace D2L.Security.OAuth2.Keys.Default {
public sealed partial class TokenSigner : ITokenSigner {
private readonly IPrivateKeyProvider m_privateKeyProvider;
+ private readonly JsonWebTokenHandler m_tokenHandler = new() { SetDefaultTimesOnTokenCreation = false };
public TokenSigner(
IKeyManagementService keyManagementService
@@ -22,31 +23,28 @@ IPrivateKeyProvider privateKeyProvider
[GenerateSync]
async Task ITokenSigner.SignAsync( UnsignedToken token ) {
- JwtSecurityToken jwt;
using( D2LSecurityToken securityToken = await m_privateKeyProvider
.GetSigningCredentialsAsync()
.ConfigureAwait( false )
) {
- jwt = new JwtSecurityToken(
- issuer: token.Issuer,
- audience: token.Audience,
- claims: Enumerable.Empty(),
- notBefore: token.NotBefore,
- expires: token.ExpiresAt,
- signingCredentials: securityToken.GetSigningCredentials()
- );
+ SecurityTokenDescriptor jwt = new SecurityTokenDescriptor() {
+ Issuer = token.Issuer,
+ Audience = token.Audience,
+ NotBefore = token.NotBefore,
+ Expires = token.ExpiresAt,
+ SigningCredentials = securityToken.GetSigningCredentials(),
+ Claims = new Dictionary(),
+ };
var claims = token.Claims;
foreach( var claim in claims ) {
- if( jwt.Payload.ContainsKey( claim.Key ) ) {
+ if( jwt.Claims.ContainsKey( claim.Key ) ) {
throw new ValidationException( $"'{claim.Key}' is already part of the payload" );
}
- jwt.Payload.Add( claim.Key, claim.Value );
+ jwt.Claims.Add( claim.Key, claim.Value );
}
- var jwtHandler = new JwtSecurityTokenHandler();
-
- string signedRawToken = jwtHandler.WriteToken( jwt );
+ string signedRawToken = m_tokenHandler.CreateToken( jwt );
return signedRawToken;
}
diff --git a/src/D2L.Security.OAuth2/Provisioning/Default/CachedAccessTokenProvider.cs b/src/D2L.Security.OAuth2/Provisioning/Default/CachedAccessTokenProvider.cs
index f579d624..3ca9cd4e 100644
--- a/src/D2L.Security.OAuth2/Provisioning/Default/CachedAccessTokenProvider.cs
+++ b/src/D2L.Security.OAuth2/Provisioning/Default/CachedAccessTokenProvider.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
-using System.IdentityModel.Tokens.Jwt;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@@ -10,16 +10,12 @@
using D2L.Services;
using D2L.CodeStyle.Annotations;
-#if DNXCORE50
-using System.IdentityModel.Tokens.Jwt;
-#endif
-
namespace D2L.Security.OAuth2.Provisioning.Default {
internal sealed partial class CachedAccessTokenProvider : IAccessTokenProvider {
private readonly INonCachingAccessTokenProvider m_accessTokenProvider;
private readonly Uri m_authEndpoint;
private readonly TimeSpan m_tokenRefreshGracePeriod;
- private readonly JwtSecurityTokenHandler m_tokenHandler;
+ private readonly JsonWebTokenHandler m_tokenHandler = new();
public CachedAccessTokenProvider(
INonCachingAccessTokenProvider accessTokenProvider,
@@ -29,8 +25,6 @@ TimeSpan tokenRefreshGracePeriod
m_accessTokenProvider = accessTokenProvider;
m_authEndpoint = authEndpoint;
m_tokenRefreshGracePeriod = tokenRefreshGracePeriod;
-
- m_tokenHandler = new JwtSecurityTokenHandler();
}
[GenerateSync]
diff --git a/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessToken.cs b/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessToken.cs
index 6c05b295..03fc2dac 100644
--- a/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessToken.cs
+++ b/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessToken.cs
@@ -1,23 +1,18 @@
using System;
using System.Collections.Generic;
-using Microsoft.IdentityModel.Tokens;
-using System.IdentityModel.Tokens.Jwt;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Security.Claims;
using D2L.CodeStyle.Annotations;
using static D2L.CodeStyle.Annotations.Objects;
-#if DNXCORE50
-using System.IdentityModel.Tokens.Jwt;
-#endif
-
namespace D2L.Security.OAuth2.Validation.AccessTokens {
[Immutable]
internal sealed class AccessToken : IAccessToken {
[Mutability.Audited( "Todd Lang", "02-Mar-2018", ".Net class we can't modify, but is used immutably." )]
- private readonly JwtSecurityToken m_inner;
+ private readonly JsonWebToken m_inner;
private readonly IAccessToken m_this;
- internal AccessToken( JwtSecurityToken jwtSecurityToken ) {
+ internal AccessToken( JsonWebToken jwtSecurityToken ) {
m_inner = jwtSecurityToken;
m_this = this;
}
@@ -35,7 +30,7 @@ IEnumerable IAccessToken.Claims {
}
string IAccessToken.SensitiveRawAccessToken {
- get { return m_inner.RawData; }
+ get { return m_inner.EncodedToken; }
}
}
}
diff --git a/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessTokenValidator.cs b/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessTokenValidator.cs
index dcdf26f5..f1d6964f 100644
--- a/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessTokenValidator.cs
+++ b/src/D2L.Security.OAuth2/Validation/AccessTokens/AccessTokenValidator.cs
@@ -1,19 +1,24 @@
using System;
using System.Collections.Immutable;
using Microsoft.IdentityModel.Tokens;
-using System.IdentityModel.Tokens.Jwt;
-using System.Threading;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Threading.Tasks;
using D2L.Security.OAuth2.Keys.Default;
using D2L.Security.OAuth2.Validation.Exceptions;
using D2L.Services;
using D2L.CodeStyle.Annotations;
-#if DNXCORE50
-using System.IdentityModel.Tokens.Jwt;
-#endif
-
namespace D2L.Security.OAuth2.Validation.AccessTokens {
+ internal static class JsonWebTokenHandlerExtensions {
+
+ public static TokenValidationResult ValidateToken(
+ this JsonWebTokenHandler @this,
+ SecurityToken token,
+ TokenValidationParameters validationParameters
+ ) => @this.ValidateTokenAsync( token, validationParameters ).ConfigureAwait( false ).GetAwaiter().GetResult();
+
+ }
+
internal sealed partial class AccessTokenValidator : IAccessTokenValidator {
internal static readonly ImmutableHashSet ALLOWED_SIGNATURE_ALGORITHMS = ImmutableHashSet.Create(
SecurityAlgorithms.RsaSha256,
@@ -23,10 +28,7 @@ internal sealed partial class AccessTokenValidator : IAccessTokenValidator {
);
private readonly IPublicKeyProvider m_publicKeyProvider;
- private readonly ThreadLocal m_tokenHandler = new ThreadLocal(
- valueFactory: () => new JwtSecurityTokenHandler(),
- trackAllValues: false
- );
+ private readonly JsonWebTokenHandler m_tokenHandler = new();
public AccessTokenValidator(
IPublicKeyProvider publicKeyProvider
@@ -41,31 +43,27 @@ IPublicKeyProvider publicKeyProvider
async Task IAccessTokenValidator.ValidateAsync(
string token
) {
- var tokenHandler = m_tokenHandler.Value;
-
- if( !tokenHandler.CanReadToken( token ) ) {
+ if( !m_tokenHandler.CanReadToken( token ) ) {
throw new ValidationException( "Couldn't parse token" );
}
- var unvalidatedToken = ( JwtSecurityToken )tokenHandler.ReadToken(
+ var unvalidatedToken = ( JsonWebToken )m_tokenHandler.ReadToken(
token
);
- if( !ALLOWED_SIGNATURE_ALGORITHMS.Contains( unvalidatedToken.SignatureAlgorithm ) ) {
+ if( !ALLOWED_SIGNATURE_ALGORITHMS.Contains( unvalidatedToken.Alg ) ) {
string message = string.Format(
"Signature algorithm '{0}' is not supported. Permitted algorithms are '{1}'",
- unvalidatedToken.SignatureAlgorithm,
+ unvalidatedToken.Alg,
string.Join( ",", ALLOWED_SIGNATURE_ALGORITHMS )
);
throw new InvalidTokenException( message );
}
- if( !unvalidatedToken.Header.ContainsKey( "kid" ) ) {
+ if( !unvalidatedToken.TryGetHeaderValue( "kid", out string keyId ) ) {
throw new InvalidTokenException( "KeyId not found in token" );
}
- string keyId = unvalidatedToken.Header[ "kid" ].ToString();
-
using D2LSecurityToken signingKey = ( await m_publicKeyProvider
.GetByIdAsync( keyId )
.ConfigureAwait( false )
@@ -82,12 +80,14 @@ string token
IAccessToken accessToken;
try {
- tokenHandler.ValidateToken(
- token,
- validationParameters,
- out SecurityToken securityToken
- );
- accessToken = new AccessToken( ( JwtSecurityToken )securityToken );
+ TokenValidationResult validationResult = await m_tokenHandler.ValidateTokenAsync(
+ unvalidatedToken,
+ validationParameters
+ ).ConfigureAwait( false );
+ if( !validationResult.IsValid ) {
+ throw validationResult.Exception;
+ }
+ accessToken = new AccessToken( (JsonWebToken)validationResult.SecurityToken );
} catch( SecurityTokenExpiredException e ) {
throw new ExpiredTokenException( e );
} catch( SecurityTokenNotYetValidException e ) {
diff --git a/test/D2L.Security.OAuth2.IntegrationTests/D2L.Security.OAuth2.IntegrationTests.csproj b/test/D2L.Security.OAuth2.IntegrationTests/D2L.Security.OAuth2.IntegrationTests.csproj
index 9f04e205..5bd67272 100644
--- a/test/D2L.Security.OAuth2.IntegrationTests/D2L.Security.OAuth2.IntegrationTests.csproj
+++ b/test/D2L.Security.OAuth2.IntegrationTests/D2L.Security.OAuth2.IntegrationTests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/test/D2L.Security.OAuth2.UnitTests/D2L.Security.OAuth2.UnitTests.csproj b/test/D2L.Security.OAuth2.UnitTests/D2L.Security.OAuth2.UnitTests.csproj
index bc940622..3f43dc20 100644
--- a/test/D2L.Security.OAuth2.UnitTests/D2L.Security.OAuth2.UnitTests.csproj
+++ b/test/D2L.Security.OAuth2.UnitTests/D2L.Security.OAuth2.UnitTests.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/test/D2L.Security.OAuth2.UnitTests/Keys/Default/PrivateKeyProviderTests.Concurrency.cs b/test/D2L.Security.OAuth2.UnitTests/Keys/Default/PrivateKeyProviderTests.Concurrency.cs
index 04ebf786..d43b6b6c 100644
--- a/test/D2L.Security.OAuth2.UnitTests/Keys/Default/PrivateKeyProviderTests.Concurrency.cs
+++ b/test/D2L.Security.OAuth2.UnitTests/Keys/Default/PrivateKeyProviderTests.Concurrency.cs
@@ -5,7 +5,7 @@
using System.Threading;
using D2L.Services;
using NUnit.Framework;
-using System.IdentityModel.Tokens.Jwt;
+using Microsoft.IdentityModel.JsonWebTokens;
namespace D2L.Security.OAuth2.Keys.Default {
[TestFixture]
@@ -82,13 +82,13 @@ int threadNumber
}
private static string Sign( D2LSecurityToken securityToken ) {
- JwtSecurityToken jwt = new JwtSecurityToken(
- issuer: TEST_ISSUER,
- signingCredentials: securityToken.GetSigningCredentials()
- );
+ SecurityTokenDescriptor jwt = new() {
+ Issuer = TEST_ISSUER,
+ SigningCredentials = securityToken.GetSigningCredentials()
+ };
- JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler();
- string signedToken = jwtHandler.WriteToken( jwt );
+ JsonWebTokenHandler jwtHandler = new();
+ string signedToken = jwtHandler.CreateToken( jwt );
return signedToken;
}
@@ -97,7 +97,7 @@ private static void AssertSignatureVerifiable(
D2LSecurityToken securityToken,
string signedToken
) {
- JwtSecurityTokenHandler validationTokenHandler = new JwtSecurityTokenHandler();
+ JsonWebTokenHandler validationTokenHandler = new();
TokenValidationParameters validationParameters = new TokenValidationParameters() {
ValidateAudience = false,
ValidateIssuer = false,
@@ -105,13 +105,15 @@ string signedToken
RequireSignedTokens = true,
IssuerSigningKey = securityToken
};
- validationTokenHandler.ValidateToken(
+ TokenValidationResult validationResult = validationTokenHandler.ValidateToken(
signedToken,
- validationParameters,
- out SecurityToken validatedToken
+ validationParameters
);
+ if( !validationResult.IsValid ) {
+ throw validationResult.Exception;
+ }
- JwtSecurityToken validatedJwt = validatedToken as JwtSecurityToken;
+ JsonWebToken validatedJwt = validationResult.SecurityToken as JsonWebToken;
Assert.AreEqual( TEST_ISSUER, validatedJwt.Issuer );
}
}
diff --git a/test/D2L.Security.OAuth2.UnitTests/Provisioning/Default/AccessTokenProviderTests.cs b/test/D2L.Security.OAuth2.UnitTests/Provisioning/Default/AccessTokenProviderTests.cs
index 89e38962..60ae7b77 100644
--- a/test/D2L.Security.OAuth2.UnitTests/Provisioning/Default/AccessTokenProviderTests.cs
+++ b/test/D2L.Security.OAuth2.UnitTests/Provisioning/Default/AccessTokenProviderTests.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
-using System.IdentityModel.Tokens.Jwt;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@@ -25,7 +25,7 @@ private static class TestData {
private IPublicKeyDataProvider m_publicKeyDataProvider;
private ITokenSigner m_tokenSigner;
private INonCachingAccessTokenProvider m_accessTokenProvider;
- private JwtSecurityToken m_actualAssertion;
+ private JsonWebToken m_actualAssertion;
[SetUp]
public void SetUp() {
@@ -33,8 +33,8 @@ public void SetUp() {
clientMock
.Setup( x => x.ProvisionAccessTokenAsync( It.IsAny(), It.IsAny>() ) )
.Callback>( ( assertion, _ ) => {
- var tokenHandler = new JwtSecurityTokenHandler();
- m_actualAssertion = ( JwtSecurityToken )tokenHandler.ReadToken( assertion );
+ var tokenHandler = new JsonWebTokenHandler();
+ m_actualAssertion = ( JsonWebToken )tokenHandler.ReadToken( assertion );
} )
.ReturnsAsync( value: null );
@@ -63,7 +63,7 @@ await m_accessTokenProvider
var publicKeys = ( await m_publicKeyDataProvider.GetAllAsync().ConfigureAwait( false ) ).ToList();
string expectedKeyId = publicKeys.First().Id.ToString();
- string actualKeyId = m_actualAssertion.Header.Kid;
+ string actualKeyId = m_actualAssertion.GetHeaderValue( "kid" );
Assert.AreEqual( 1, publicKeys.Count );
Assert.AreEqual( expectedKeyId, actualKeyId );
@@ -90,7 +90,7 @@ await m_accessTokenProvider
AssertClaimEquals( m_actualAssertion, Constants.Claims.USER_ID, TestData.USER );
}
- private void AssertClaimEquals( JwtSecurityToken token, string name, string value ) {
+ private void AssertClaimEquals( JsonWebToken token, string name, string value ) {
Claim claim = token.Claims.FirstOrDefault( c => c.Type == name );
Assert.IsNotNull( claim );
Assert.AreEqual( value, claim.Value );
diff --git a/test/D2L.Security.OAuth2.UnitTests/Validation/AccessTokenValidatorTests.cs b/test/D2L.Security.OAuth2.UnitTests/Validation/AccessTokenValidatorTests.cs
index e906a707..9c5a9642 100644
--- a/test/D2L.Security.OAuth2.UnitTests/Validation/AccessTokenValidatorTests.cs
+++ b/test/D2L.Security.OAuth2.UnitTests/Validation/AccessTokenValidatorTests.cs
@@ -1,6 +1,6 @@
using System;
using Microsoft.IdentityModel.Tokens;
-using System.IdentityModel.Tokens.Jwt;
+using Microsoft.IdentityModel.JsonWebTokens;
using System.Threading.Tasks;
using D2L.Security.OAuth2.Keys.Default;
using D2L.Security.OAuth2.TestUtilities;
@@ -83,14 +83,14 @@ private async Task RunTest(
signingCredentials = signingToken.GetSigningCredentials();
}
- var jwtToken = new JwtSecurityToken(
- issuer: "someissuer",
- signingCredentials: signingCredentials,
- expires: jwtExpiry
- );
+ SecurityTokenDescriptor jwtToken = new() {
+ Issuer = "someissuer",
+ SigningCredentials = signingCredentials,
+ Expires = jwtExpiry
+ };
- var tokenHandler = new JwtSecurityTokenHandler();
- string serializedJwt = tokenHandler.WriteToken( jwtToken );
+ JsonWebTokenHandler tokenHandler = new() { SetDefaultTimesOnTokenCreation = false };
+ string serializedJwt = tokenHandler.CreateToken( jwtToken );
IPublicKeyProvider publicKeyProvider = PublicKeyProviderMock.Create(
m_jwksEndpoint,