Skip to content

Commit

Permalink
Merged PR 10258: Compatibility with 6x for bool claims (#2367)
Browse files Browse the repository at this point in the history
Compatibility with 6x for bool claims (#2367)

* Compatibility with 6x for bool claims

Fix behavior where ToString is used on bool values
resulting in the value being True/False instead of true/false

* Use direct ClaimValueType

---------

Co-authored-by: Keegan Caruso <keegancaruso@microsoft.com>

----
#### AI-Generated Description
The pull request modifies the handling of boolean claims in the JsonWebToken library. It changes the encoding and decoding of boolean values to use lowercase "true" and "false" instead of uppercase "True" and "False". It also adds unit tests to verify the expected behavior. The main changes are in the following files:
- **JsonWebTokenTests.cs**: Adds a new test method `BoolClaimsEncodedAsExpected` to check the encoding and decoding of boolean claims.
- **JwtPayload.cs**: Adds logic to handle boolean values in the `CreateClaims` and `CreateClaimFromObject` methods.
- **JsonClaimSet.cs**: Adds logic to handle boolean values in the `CreateClaimFromObject` and `CreateClaimFromJsonElement` methods.
  • Loading branch information
Brent Schmaltz committed Oct 17, 2023
1 parent 5c1ea4a commit 97888a2
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 8 deletions.
12 changes: 9 additions & 3 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ internal static void CreateClaimFromObject(List<Claim> claims, string claimType,
else if (value is long l)
claims.Add(new Claim(claimType, l.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64, issuer, issuer));
else if (value is bool b)
claims.Add(new Claim(claimType, b.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Boolean, issuer, issuer));
{
// Can't just use ToString or bools will get encoded as True/False instead of true/false.
if (b)
claims.Add(new Claim(claimType, "true", ClaimValueTypes.Boolean, issuer, issuer));
else
claims.Add(new Claim(claimType, "false", ClaimValueTypes.Boolean, issuer, issuer));
}
else if (value is double d)
claims.Add(new Claim(claimType, d.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer));
else if (value is DateTime dt)
Expand Down Expand Up @@ -108,9 +114,9 @@ internal static Claim CreateClaimFromJsonElement(string claimType, string issuer
else if (jsonElement.ValueKind == JsonValueKind.Object)
return new Claim(claimType, jsonElement.ToString(), JsonClaimValueTypes.Json, issuer, issuer);
else if (jsonElement.ValueKind == JsonValueKind.False)
return new Claim(claimType, "False", ClaimValueTypes.Boolean, issuer, issuer);
return new Claim(claimType, "false", ClaimValueTypes.Boolean, issuer, issuer);
else if (jsonElement.ValueKind == JsonValueKind.True)
return new Claim(claimType, "True", ClaimValueTypes.Boolean, issuer, issuer);
return new Claim(claimType, "true", ClaimValueTypes.Boolean, issuer, issuer);
else if (jsonElement.ValueKind == JsonValueKind.Number)
{
if (jsonElement.TryGetInt32(out int i))
Expand Down
15 changes: 15 additions & 0 deletions src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,14 @@ public virtual IEnumerable<Claim> Claims
}
else if (keyValuePair.Value is DateTime dateTime)
claims.Add(new Claim(keyValuePair.Key, dateTime.ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer));
else if (keyValuePair.Value is bool boolValue)
{
// Can't just use ToString or bools will get encoded as True/False instead of true/false.
if (boolValue)
claims.Add(new Claim(keyValuePair.Key, "true", ClaimValueTypes.Boolean, issuer, issuer));
else
claims.Add(new Claim(keyValuePair.Key, "false", ClaimValueTypes.Boolean, issuer, issuer));
}
else if (keyValuePair.Value != null)
claims.Add(new Claim(keyValuePair.Key, keyValuePair.Value.ToString(), GetClaimValueType(keyValuePair.Value), issuer, issuer));
}
Expand All @@ -557,6 +565,13 @@ private void AddListofObjects(string key, IEnumerable<object> objects, List<Clai
{
if (obj is string claimValue)
claims.Add(new Claim(key, claimValue, ClaimValueTypes.String, issuer, issuer));
else if (obj is bool boolValue)
{
if (boolValue)
claims.Add(new Claim(key, "true", ClaimValueTypes.Boolean, issuer, issuer));
else
claims.Add(new Claim(key, "false", ClaimValueTypes.Boolean, issuer, issuer));
}
else if (obj is DateTime dateTimeValue)
claims.Add(new Claim(key, dateTimeValue.ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer));
else if (obj is JsonElement jsonElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,39 @@ public class JsonWebTokenTests
new Claim("float", "42", ClaimValueTypes.Integer32, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("integer", "42", ClaimValueTypes.Integer32, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("nill", "", JsonClaimValueTypes.JsonNull, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("bool", "True", ClaimValueTypes.Boolean, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("bool", "true", ClaimValueTypes.Boolean, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("dateTime", dateTime.ToString(), ClaimValueTypes.String, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
new Claim("dateTimeIso8061", dateTime.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, "LOCAL AUTHORITY", "LOCAL AUTHORITY"),
};

[Fact]
public void BoolClaimsEncodedAsExpected()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128)));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var claims = new[] { new Claim("testClaim", "true", ClaimValueTypes.Boolean), new Claim("testClaim2", "True", ClaimValueTypes.Boolean) };
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
{
SigningCredentials = creds,
Subject = new ClaimsIdentity(claims),
Expires = (new DateTime(2038, 1, 20)).ToUniversalTime(),
};

JsonWebTokenHandler handler = new();
string jwt = handler.CreateToken(tokenDescriptor);
JsonWebToken jsonWebToken = new JsonWebToken(jwt);

var claimSet = jsonWebToken.Claims;

// Will throw if can't find.
var testClaim = claimSet.First(c => c.Type == "testClaim");
Assert.Equal("true", testClaim.Value);

var testClaim2 = claimSet.First(c => c.Type == "testClaim2");
Assert.Equal("true", testClaim2.Value);
}

[Fact]
public void DateTime2038Issue()
{
Expand Down
8 changes: 4 additions & 4 deletions test/System.IdentityModel.Tokens.Jwt.Tests/JwtPayloadTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ public static TheoryData<string, List<Claim>, JwtPayload, JwtPayload> PayloadDat
new List<Claim>
{
new Claim("ClaimValueTypes.String", "ClaimValueTypes.String.Value", ClaimValueTypes.String),
new Claim("ClaimValueTypes.Boolean.true", "True", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes.Boolean.false", "False", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes.Boolean.true", "true", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes.Boolean.false", "false", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes.Double", "123.4", ClaimValueTypes.Double),
new Claim("ClaimValueTypes.int.MaxValue", intMaxValue, ClaimValueTypes.Integer32),
new Claim("ClaimValueTypes.int.MinValue", intMinValue, ClaimValueTypes.Integer32),
Expand Down Expand Up @@ -300,8 +300,8 @@ public static TheoryData<string, List<Claim>, JwtPayload, JwtPayload> PayloadDat
new Claim("ClaimValueTypes", longValue, ClaimValueTypes.Integer64),
new Claim("ClaimValueTypes", "132.64", ClaimValueTypes.Double),
new Claim("ClaimValueTypes", "-132.64", ClaimValueTypes.Double),
new Claim("ClaimValueTypes", "True", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes", "False", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes", "true", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes", "false", ClaimValueTypes.Boolean),
new Claim("ClaimValueTypes", "2019-11-15T14:31:21.6101326Z", ClaimValueTypes.DateTime),
new Claim("ClaimValueTypes", "2019-11-15", ClaimValueTypes.String),
new Claim("ClaimValueTypes", @"{""name3.1"":""value3.1""}", JsonClaimValueTypes.Json),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Logging;
Expand All @@ -13,6 +14,30 @@ namespace System.IdentityModel.Tokens.Jwt.Tests
{
public class JwtSecurityTokenTests
{
[Fact]
public void BoolClaimsEncodedAsExpected()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128)));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var claims = new[] { new Claim("testClaim", "true", ClaimValueTypes.Boolean), new Claim("testClaim2", "True", ClaimValueTypes.Boolean) };
var token = new JwtSecurityToken(
issuer: "issuer.contoso.com",
audience: "audience.contoso.com",
claims: claims,
expires: (new DateTime(2038, 1, 20)).ToUniversalTime(),
signingCredentials: creds);

var claimSet = token.Claims;

// Will throw if can't find.
var testClaim = claimSet.First(c => c.Type == "testClaim");
Assert.Equal("true", testClaim.Value);

var testClaim2 = claimSet.First(c => c.Type == "testClaim2");
Assert.Equal("true", testClaim2.Value);
}

[Fact]
public void DateTime2038Issue()
{
Expand Down

0 comments on commit 97888a2

Please sign in to comment.