diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs index 1fb5a7410d..dac70e195b 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs @@ -19,6 +19,9 @@ internal JsonClaimSet CreatePayloadClaimSet(byte[] bytes, int length) internal JsonClaimSet CreatePayloadClaimSet(ReadOnlySpan byteSpan) { + if (byteSpan.Length == 0) + return new JsonClaimSet([]); + Utf8JsonReader reader = new(byteSpan); if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, true)) throw LogHelper.LogExceptionMessage( diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs index 21537c7d77..791d8ffa66 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs @@ -130,7 +130,7 @@ public JsonWebToken(string header, string payload) if (string.IsNullOrEmpty(header)) throw LogHelper.LogArgumentNullException(nameof(header)); - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); var encodedHeader = Base64UrlEncoder.Encode(header); diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs index 4bce5b83f6..4df5e9d432 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs @@ -34,7 +34,7 @@ public partial class JsonWebTokenHandler : TokenHandler /// A JWS in Compact Serialization format. public virtual string CreateToken(string payload) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); return CreateToken( @@ -59,7 +59,7 @@ public virtual string CreateToken( string payload, IDictionary additionalHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = additionalHeaderClaims ?? throw LogHelper.LogArgumentNullException(nameof(additionalHeaderClaims)); @@ -85,7 +85,7 @@ public virtual string CreateToken( string payload, SigningCredentials signingCredentials) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = signingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(signingCredentials)); @@ -118,7 +118,7 @@ public virtual string CreateToken( SigningCredentials signingCredentials, IDictionary additionalHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = signingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(signingCredentials)); @@ -296,7 +296,7 @@ public virtual string CreateToken( string payload, EncryptingCredentials encryptingCredentials) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = encryptingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(encryptingCredentials)); @@ -329,7 +329,7 @@ public virtual string CreateToken( EncryptingCredentials encryptingCredentials, IDictionary additionalHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = encryptingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(encryptingCredentials)); @@ -360,7 +360,7 @@ public virtual string CreateToken( SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = signingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(signingCredentials)); @@ -397,7 +397,7 @@ public virtual string CreateToken( EncryptingCredentials encryptingCredentials, IDictionary additionalHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); _ = signingCredentials ?? throw LogHelper.LogArgumentNullException(nameof(signingCredentials)); @@ -426,7 +426,7 @@ public virtual string CreateToken( EncryptingCredentials encryptingCredentials, string compressionAlgorithm) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); if (string.IsNullOrEmpty(compressionAlgorithm)) @@ -462,7 +462,7 @@ public virtual string CreateToken( EncryptingCredentials encryptingCredentials, string compressionAlgorithm) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); if (string.IsNullOrEmpty(compressionAlgorithm)) @@ -507,7 +507,7 @@ public virtual string CreateToken( IDictionary additionalHeaderClaims, IDictionary additionalInnerHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); if (string.IsNullOrEmpty(compressionAlgorithm)) @@ -552,7 +552,7 @@ public virtual string CreateToken( string compressionAlgorithm, IDictionary additionalHeaderClaims) { - if (string.IsNullOrEmpty(payload)) + if (payload == null) throw LogHelper.LogArgumentNullException(nameof(payload)); if (string.IsNullOrEmpty(compressionAlgorithm)) diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index 0c23791609..6f94258c27 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -1626,7 +1626,6 @@ public static TheoryData CreateJWEWithPayloadStringTheory { new CreateTokenTheoryData { - First = true, TestId = "JsonPayload", Payload = Default.PayloadString, TokenDescriptor = new SecurityTokenDescriptor @@ -1637,8 +1636,7 @@ public static TheoryData CreateJWEWithPayloadStringTheory }, new CreateTokenTheoryData { - First = true, - TestId = "JsonPayload", + TestId = "JsonPayload_No_Cty", Payload = Default.PayloadString, TokenDescriptor = new SecurityTokenDescriptor { @@ -2022,32 +2020,6 @@ public static TheoryData CreateJWSUsingSecurityTokenDescr new Claim(JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(Default.Expires).ToString(), ClaimValueTypes.String, Default.Issuer, Default.Issuer), }, "AuthenticationTypes.Federation") }, - TokenDescriptor6x = new SecurityTokenDescriptor - { - SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, - Claims = new Dictionary() - { - { JwtRegisteredClaimNames.Email, "Bob@contoso.com" }, - { JwtRegisteredClaimNames.GivenName, "Bob" }, - { JwtRegisteredClaimNames.Iss, Default.Issuer }, - { JwtRegisteredClaimNames.Aud, JArray.FromObject(Default.Audiences) }, - { JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(Default.IssueInstant).ToString() }, - { JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(Default.NotBefore).ToString()}, - { JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(Default.Expires).ToString() }, - }, - Subject = new CaseSensitiveClaimsIdentity(new List() - { - new Claim(JwtRegisteredClaimNames.Email, "Bob@contoso.com", ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.GivenName, "Bob", ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Iss, "Issuer", ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Aud.ToUpper(), "Audience1", ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Aud.ToUpper(), "Audience2", ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(Default.IssueInstant).ToString(), ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(Default.NotBefore).ToString(), ClaimValueTypes.String, Default.Issuer, Default.Issuer), - new Claim(JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(Default.Expires).ToString(), ClaimValueTypes.String, Default.Issuer, Default.Issuer), - }, "AuthenticationTypes.Federation") - }, - JsonWebTokenHandler = new JsonWebTokenHandler(), ValidationParameters = new TokenValidationParameters { @@ -2472,8 +2444,8 @@ public async Task AdditionalHeaderValues() public void RoundTripJWS() { TestUtilities.WriteHeader($"{this}.RoundTripToken"); - var context = new CompareContext(); + var context = new CompareContext(); var tokenHandler = new JsonWebTokenHandler(); var tokenValidationParameters = new TokenValidationParameters() { @@ -2486,12 +2458,158 @@ public void RoundTripJWS() string jwtString = tokenHandler.CreateToken(Default.PayloadString, KeyingMaterial.JsonWebKeyRsa256SigningCredentials); var tokenValidationResult = tokenHandler.ValidateTokenAsync(jwtString, tokenValidationParameters).Result; var validatedToken = tokenValidationResult.SecurityToken as JsonWebToken; - var claimsIdentity = tokenValidationResult.ClaimsIdentity; - IdentityComparer.AreEqual(Default.PayloadClaimsIdentity, claimsIdentity, context); + IdentityComparer.AreEqual(Default.PayloadClaimsIdentity, tokenValidationResult.ClaimsIdentity, context); IdentityComparer.AreEqual(Default.PayloadString, Base64UrlEncoder.Decode(validatedToken.EncodedPayload), context); + TestUtilities.AssertFailIfErrors(context); } + // Test ensure paths to creating a token with an empty payload are successful. + [Theory, MemberData(nameof(RoundTripWithEmptyPayloadTestCases))] + public void RoundTripWithEmptyPayload(CreateTokenTheoryData theoryData) + { + TestUtilities.WriteHeader($"{this}.RoundTripWithEmptyPayload"); + + var context = new CompareContext(); + var jwtString = JsonWebTokenHandler.CreateToken( + theoryData.Payload, + theoryData.SigningCredentials, + theoryData.EncryptingCredentials, + theoryData.CompressionAlgorithm, + theoryData.AdditionalHeaderClaims, + theoryData.AdditionalInnerHeaderClaims, + "JWT"); + + var tokenValidationResult = theoryData.JsonWebTokenHandler.ValidateTokenAsync(jwtString, theoryData.ValidationParameters).Result; + var validatedToken = tokenValidationResult.SecurityToken as JsonWebToken; + var claimsIdentity = tokenValidationResult.ClaimsIdentity; + IdentityComparer.AreEqual(new ClaimsIdentity("AuthenticationTypes.Federation"), claimsIdentity, context); + IdentityComparer.AreEqual("", Base64UrlEncoder.Decode(validatedToken.EncodedPayload), context); + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData RoundTripWithEmptyPayloadTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + + Dictionary additionalInnerHeaderClaims = new Dictionary() + { + { "intInnerHeader", 1233 }, + { "stringInnerHeader", "stringInnerHeader" } + }; + + Dictionary additionalHeaderClaims = new Dictionary() + { + { "int", 123 }, + { "string", "string" } + }; + + EncryptingCredentials encryptingCredentials = new EncryptingCredentials(KeyingMaterial.DefaultX509Key_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256); + + TokenValidationParameters validationParameters = new TokenValidationParameters + { + IssuerSigningKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, + RequireSignedTokens = false, + TokenDecryptionKey = KeyingMaterial.DefaultX509Key_2048, + ValidateAudience = false, + ValidateLifetime = false, + ValidateIssuer = false + }; + + theoryData.Add(new CreateTokenTheoryData("EmptyPayload") + { + Payload = "", + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("additionalHeaderClaims") + { + AdditionalHeaderClaims = additionalHeaderClaims, + Payload = "", + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentials") + { + EncryptingCredentials = encryptingCredentials, + Payload = "", + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsAdditionalHeaderClaims") + { + EncryptingCredentials = encryptingCredentials, + Payload = "", + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsCompression") + { + CompressionAlgorithm = CompressionAlgorithms.Deflate, + EncryptingCredentials = encryptingCredentials, + Payload = "", + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsSigningCredentials") + { + EncryptingCredentials = encryptingCredentials, + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsSigningCredentialsAdditionalHeaderClaims") + { + AdditionalHeaderClaims = additionalHeaderClaims, + EncryptingCredentials = encryptingCredentials, + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsSigningCredentialsCompression") + { + CompressionAlgorithm = CompressionAlgorithms.Deflate, + EncryptingCredentials = encryptingCredentials, + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("encryptingCredentialsSigningCredentialsCompressionAddionalHeaderClaimsAdditionalInnerHeaderClaims") + { + AdditionalInnerHeaderClaims = additionalInnerHeaderClaims, + AdditionalHeaderClaims = additionalHeaderClaims, + CompressionAlgorithm = CompressionAlgorithms.Deflate, + EncryptingCredentials = encryptingCredentials, + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("signingCredentials") + { + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + theoryData.Add(new CreateTokenTheoryData("signingCredentialsAdditionalHeaderClaims") + { + AdditionalHeaderClaims = additionalHeaderClaims, + Payload = "", + SigningCredentials = KeyingMaterial.JsonWebKeyRsa256SigningCredentials, + ValidationParameters = validationParameters + }); + + return theoryData; + } + } + [Theory, MemberData(nameof(RoundTripJWEDirectTestCases))] public void RoundTripJWEInnerJWSDirect(CreateTokenTheoryData theoryData) { @@ -4276,6 +4394,8 @@ public CreateTokenTheoryData(string testId) : base(testId) public Dictionary AdditionalHeaderClaims { get; set; } + public Dictionary AdditionalInnerHeaderClaims { get; set; } + public string Payload { get; set; } public string CompressionAlgorithm { get; set; } @@ -4292,9 +4412,7 @@ public CreateTokenTheoryData(string testId) : base(testId) public SecurityTokenDescriptor TokenDescriptor { get; set; } - public SecurityTokenDescriptor TokenDescriptor6x { get; set; } - - public JsonWebTokenHandler JsonWebTokenHandler { get; set; } + public JsonWebTokenHandler JsonWebTokenHandler { get; set; } = new JsonWebTokenHandler(); public JwtSecurityTokenHandler JwtSecurityTokenHandler { get; set; } diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs index c0a18f9f7e..04e9902186 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs @@ -1599,12 +1599,6 @@ public static TheoryData ParseTokenTheoryData ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14102:", typeof(JsonReaderException), true), }); - theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWSEmptyPayload)) - { - Token = EncodedJwts.JWSEmptyPayload, - ExpectedException = new ExpectedException(typeof(ArgumentException), "IDX14101:", typeof(JsonReaderException), true), - }); - theoryData.Add(new JwtTheoryData(nameof(EncodedJwts.JWEEmptyHeader)) { Token = EncodedJwts.JWEEmptyHeader,