-
Notifications
You must be signed in to change notification settings - Fork 413
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JwtSecurityTokenConverter initial commit
- Loading branch information
Keegan Caruso
committed
Jun 20, 2023
1 parent
3ffa857
commit 7fc29ae
Showing
3 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.IdentityModel.JsonWebTokens; | ||
|
||
namespace System.IdentityModel.Tokens.Jwt | ||
{ | ||
/// <summary> | ||
/// Static class to converts an object to a <see cref="JwtSecurityToken"/> | ||
/// </summary> | ||
public static class JwtSecurityTokenConverter | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of a <see cref="JwtSecurityToken"/> from a <see cref="JsonWebToken"/> | ||
/// </summary> | ||
/// <param name="token">A JSON Web Token to convert from.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="token"/> is null</exception> | ||
public static JwtSecurityToken Convert(JsonWebToken token) | ||
{ | ||
if (token == null) | ||
throw new ArgumentNullException(nameof(token)); | ||
|
||
if (token.InnerToken != null) | ||
{ | ||
var outerToken = new JwtSecurityToken(token.EncodedToken); | ||
outerToken.InnerToken = new JwtSecurityToken(token.InnerToken.EncodedToken); | ||
return outerToken; | ||
} | ||
else if (!string.IsNullOrEmpty(token.EncodedToken)) | ||
{ | ||
return new JwtSecurityToken(token.EncodedToken); | ||
} | ||
else if (token.Header != null && token.Payload != null) | ||
{ | ||
var header = JwtHeader.Deserialize(token.Header.RootElement.GetRawText()); | ||
var payload = JwtPayload.Deserialize(token.Payload.RootElement.GetRawText()); | ||
return new JwtSecurityToken(header, payload); | ||
} | ||
|
||
throw new ArgumentException("token.EncodedToken or token.Header and token.Payload must be set"); | ||
} | ||
} | ||
} |
177 changes: 177 additions & 0 deletions
177
test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenConverterTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.IdentityModel.JsonWebTokens; | ||
using Microsoft.IdentityModel.TestUtils; | ||
using Microsoft.IdentityModel.Tokens; | ||
using Xunit; | ||
|
||
namespace System.IdentityModel.Tokens.Jwt.Tests | ||
{ | ||
#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant | ||
public class JwtSecurityTokenConverterTests | ||
{ | ||
[Fact] | ||
public void JwtSecurityTokenConverter_ThrowsOnNull() | ||
{ | ||
Assert.Throws<ArgumentNullException>(() => JwtSecurityTokenConverter.Convert(null)); | ||
} | ||
|
||
[Theory, MemberData(nameof(ConverterTheoryData))] | ||
public void JsonWebTokenToJwtSecurityTokenConversions(JwtSecurityTokenConverterTheoryData theoryData) | ||
{ | ||
var output = JwtSecurityTokenConverter.Convert(theoryData.InputToken); | ||
Assert.NotNull(output); | ||
theoryData.Validator(output); | ||
} | ||
|
||
public static TheoryData<JwtSecurityTokenConverterTheoryData> ConverterTheoryData() | ||
{ | ||
var tdJwe = new SecurityTokenDescriptor | ||
{ | ||
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, | ||
EncryptingCredentials = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes256_Sha512_512, | ||
Claims = Default.PayloadDictionary | ||
}; | ||
|
||
var tdJws = new SecurityTokenDescriptor | ||
{ | ||
SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, | ||
Claims = Default.PayloadDictionary | ||
}; | ||
|
||
var handler = new JsonWebTokenHandler(); | ||
var jweTokenString = handler.CreateToken(tdJwe); | ||
var jwsTokenString = handler.CreateToken(tdJws); | ||
|
||
var validationParameters = new TokenValidationParameters | ||
{ | ||
RequireExpirationTime = false, | ||
ValidateAudience = false, | ||
ValidateIssuer = false, | ||
TokenDecryptionKey = KeyingMaterial.DefaultSymmetricEncryptingCreds_Aes256_Sha512_512.Key, | ||
IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key | ||
}; | ||
|
||
var result = handler.ValidateToken(jweTokenString, validationParameters); | ||
var jweToken = result.SecurityToken as JsonWebToken; | ||
|
||
result = handler.ValidateToken(jwsTokenString, validationParameters); | ||
var jwsTokenFromString = result.SecurityToken as JsonWebToken; | ||
|
||
var jwsTokenFromHeaderAndPayload = new JsonWebToken( | ||
Default.PayloadString, | ||
new Microsoft.IdentityModel.Json.Linq.JObject | ||
{ | ||
{ JwtHeaderParameterNames.Alg, SecurityAlgorithms.Sha512 }, | ||
{ JwtHeaderParameterNames.Kid, Default.AsymmetricSigningKey.KeyId }, | ||
{ JwtHeaderParameterNames.Typ, JwtConstants.HeaderType } | ||
}.ToString(Microsoft.IdentityModel.Json.Formatting.None)); | ||
|
||
return new TheoryData<JwtSecurityTokenConverterTheoryData> | ||
{ | ||
new JwtSecurityTokenConverterTheoryData | ||
{ | ||
First = true, | ||
TestId = "JweToJwe", | ||
InputToken = jweToken, | ||
Validator = (token) => | ||
{ | ||
Assert.NotNull(token.InnerToken); | ||
|
||
foreach (var header in jweToken.Header.Claims((string)Default.PayloadDictionary[JwtRegisteredClaimNames.Iss])) | ||
{ | ||
Assert.True(token.Header.ContainsKey(header.Type)); | ||
var otherHeader = token.Header[header.Type]; | ||
Assert.Equal(header.Value, otherHeader); | ||
} | ||
|
||
foreach (var header in jweToken.InnerToken.Header.Claims((string)Default.PayloadDictionary[JwtRegisteredClaimNames.Iss])) | ||
{ | ||
Assert.True(token.InnerToken.Header.ContainsKey(header.Type)); | ||
var otherHeader = token.InnerToken.Header[header.Type]; | ||
Assert.Equal(header.Value, otherHeader); | ||
} | ||
|
||
var jweTokenClaims = new List<Security.Claims.Claim>(jweToken.Claims).ToDictionary(c => c.Type, c => c.Value); | ||
var otherTokenClaims = new List<Security.Claims.Claim>(token.Claims).ToDictionary(c => c.Type, c => c.Value);; | ||
|
||
Assert.Equal(jweTokenClaims.Count, otherTokenClaims.Count); | ||
|
||
foreach (var claim in jweTokenClaims) | ||
{ | ||
Assert.True(otherTokenClaims.ContainsKey(claim.Key)); | ||
Assert.True(otherTokenClaims[claim.Key] == claim.Value); | ||
} | ||
|
||
} | ||
}, | ||
new JwtSecurityTokenConverterTheoryData | ||
{ | ||
TestId = "JwsCreatedFromString", | ||
InputToken = jwsTokenFromString, | ||
Validator = (token) => | ||
{ | ||
Assert.Null(token.InnerToken); | ||
|
||
foreach (var header in jwsTokenFromString.Header.Claims((string)Default.PayloadDictionary[JwtRegisteredClaimNames.Iss])) | ||
{ | ||
Assert.True(token.Header.ContainsKey(header.Type)); | ||
var otherHeader = token.Header[header.Type]; | ||
Assert.Equal(header.Value, otherHeader); | ||
} | ||
|
||
var jwsTokenFromStringClaims = new List<Security.Claims.Claim>(jwsTokenFromString.Claims).ToDictionary(c => c.Type, c => c.Value); | ||
var otherTokenClaims = new List<Security.Claims.Claim>(token.Claims).ToDictionary(c => c.Type, c => c.Value);; | ||
|
||
Assert.Equal(jwsTokenFromStringClaims.Count, otherTokenClaims.Count); | ||
|
||
foreach (var claim in jwsTokenFromStringClaims) | ||
{ | ||
Assert.True(otherTokenClaims.ContainsKey(claim.Key)); | ||
Assert.True(otherTokenClaims[claim.Key] == claim.Value); | ||
} | ||
} | ||
}, | ||
new JwtSecurityTokenConverterTheoryData | ||
{ | ||
TestId = "JwsCreatedFromHeaderAndPayload", | ||
InputToken = jwsTokenFromHeaderAndPayload, | ||
Validator = (token) => | ||
{ | ||
Assert.Null(token.InnerToken); | ||
|
||
foreach (var header in jwsTokenFromHeaderAndPayload.Header.Claims((string)Default.PayloadDictionary[JwtRegisteredClaimNames.Iss])) | ||
{ | ||
Assert.True(token.Header.ContainsKey(header.Type)); | ||
var otherHeader = token.Header[header.Type]; | ||
Assert.Equal(header.Value, otherHeader); | ||
} | ||
|
||
var jwsTokenFromHeaderAndPayloadClaims = new List<Security.Claims.Claim>(jwsTokenFromHeaderAndPayload.Claims).ToDictionary(c => c.Type, c => c.Value); | ||
var otherTokenClaims = new List<Security.Claims.Claim>(token.Claims).ToDictionary(c => c.Type, c => c.Value);; | ||
|
||
Assert.Equal(jwsTokenFromHeaderAndPayloadClaims.Count, otherTokenClaims.Count); | ||
|
||
foreach (var claim in jwsTokenFromHeaderAndPayloadClaims) | ||
{ | ||
Assert.True(otherTokenClaims.ContainsKey(claim.Key)); | ||
Assert.True(otherTokenClaims[claim.Key] == claim.Value); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
|
||
public class JwtSecurityTokenConverterTheoryData : TheoryDataBase | ||
{ | ||
public JsonWebToken InputToken { get; set; } | ||
|
||
public Action<JwtSecurityToken> Validator { get; set; } | ||
} | ||
#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant | ||
} | ||
} |