Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SecurityTokenClaimsIdentity #2858

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions benchmark/Microsoft.IdentityModel.Benchmarks/ClaimsIdentityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace Microsoft.IdentityModel.Benchmarks
{
// dotnet run -c release -f net8.0 --filter Microsoft.IdentityModel.Benchmarks.ClaimsIdentityTests*

[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
public class ClaimsIdentityTests
{
private ClaimsIdentity _claimsIdentity;
private SecurityTokenClaimsIdentity _newClaimsIdentity;
private string _claimTypeToFind;
private string _claimValueToFind;
private Predicate<Claim> _findPredicate;
private Predicate<Claim> _hasClaimPredicate;

private JsonWebTokenHandler _jsonWebTokenHandler;
private string _jwsWithExtendedClaims;
private TokenValidationParameters _tokenValidationParameters;
private TokenValidationParameters _newTokenValidationParameters;

[GlobalSetup]
public async Task SetupAsync()
{
_jsonWebTokenHandler = new JsonWebTokenHandler();
_jwsWithExtendedClaims = _jsonWebTokenHandler.CreateToken(new SecurityTokenDescriptor
{
Claims = BenchmarkUtils.ClaimsExtendedExample,
SigningCredentials = BenchmarkUtils.SigningCredentialsRsaSha256,
});
_tokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = BenchmarkUtils.Audience,
ValidateLifetime = true,
ValidIssuer = BenchmarkUtils.Issuer,
IssuerSigningKey = BenchmarkUtils.SigningCredentialsRsaSha256.Key,
};
_newTokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = BenchmarkUtils.Audience,
ValidateLifetime = true,
ValidIssuer = BenchmarkUtils.Issuer,
IssuerSigningKey = BenchmarkUtils.SigningCredentialsRsaSha256.Key,
UseNewClaimsIdentityType = true,
};

_claimTypeToFind = "iss";
_claimValueToFind = BenchmarkUtils.Issuer;
_findPredicate = claim => claim.Type == _claimTypeToFind;
_hasClaimPredicate = claim => claim.Type == _claimTypeToFind && claim.Value == _claimValueToFind;

_claimsIdentity = (await _jsonWebTokenHandler.ValidateTokenAsync(_jwsWithExtendedClaims, _tokenValidationParameters).ConfigureAwait(false)).ClaimsIdentity;
_newClaimsIdentity = (await _jsonWebTokenHandler.ValidateTokenAsync(_jwsWithExtendedClaims, _newTokenValidationParameters).ConfigureAwait(false)).ClaimsIdentity as SecurityTokenClaimsIdentity;
_ = _claimsIdentity.Claims;
_ = _newClaimsIdentity.Claims;
}

[Benchmark(Baseline = true), BenchmarkCategory("FindFirst")]
public Claim ClaimsIdentity_FindFirst()
{
var temp = _claimsIdentity.FindFirst(_claimTypeToFind);
return temp;
}

//[Benchmark(Baseline = true), BenchmarkCategory("FindFirstPredicate")]
public Claim ClaimsIdentity_FindFirst_WithPredicate()
{
var temp = _claimsIdentity.FindFirst(_findPredicate);
return temp;
}

[Benchmark(Baseline = true), BenchmarkCategory("FindAll")]
public List<Claim> ClaimsIdentity_FindAll()
{
var temp = _claimsIdentity.FindAll(_claimTypeToFind).ToList();
return temp;
}

//[Benchmark(Baseline = true), BenchmarkCategory("FindAllPredicate")]
public List<Claim> ClaimsIdentity_FindAll_WithPredicate()
{
var temp = _claimsIdentity.FindAll(_findPredicate).ToList();
return temp;
}

[Benchmark(Baseline = true), BenchmarkCategory("HasPayloadClaim")]
public bool ClaimsIdentity_HasClaim()
{
var temp = _claimsIdentity.HasClaim(_claimTypeToFind, _claimValueToFind);
return temp;
}

//[Benchmark(Baseline = true), BenchmarkCategory("HasClaimPredicate")]
public bool ClaimsIdentity_HasClaim_WithPredicate()
{
var temp = _claimsIdentity.HasClaim(_hasClaimPredicate);
return temp;
}

[Benchmark, BenchmarkCategory("FindFirst")]
public Claim NewClaimsIdentity_FindFirst()
{
var temp = _newClaimsIdentity.FindFirst(_claimTypeToFind);
return temp;
}

//[Benchmark, BenchmarkCategory("FindFirstPredicate")]
public Claim NewClaimsIdentity_FindFirst_WithPredicate()
{
var temp = _newClaimsIdentity.FindFirst(_findPredicate);
return temp;
}

[Benchmark, BenchmarkCategory("FindAll")]
public List<Claim> NewClaimsIdentity_FindAll()
{
var temp = _newClaimsIdentity.FindAll(_claimTypeToFind).ToList();
return temp;
}

//[Benchmark, BenchmarkCategory("FindAllPredicate")]
public List<Claim> NewClaimsIdentity_FindAll_WithPredicate()
{
var temp = _newClaimsIdentity.FindAll(_findPredicate).ToList();
return temp;
}

[Benchmark, BenchmarkCategory("HasPayloadClaim")]
public bool NewClaimsIdentity_HasClaim()
{
var temp = _newClaimsIdentity.HasClaim(_claimTypeToFind, _claimValueToFind);
return temp;
}

//[Benchmark, BenchmarkCategory("HasClaimPredicate")]
public bool NewClaimsIdentity_HasClaim_WithPredicate()
{
var temp = _newClaimsIdentity.HasClaim(_hasClaimPredicate);
return temp;
}

//[Benchmark(Baseline = true), BenchmarkCategory("ValidateAndGetClaims")]
public async Task<IList<Claim>> ClaimsIdentity_ValidateTokenAndGetClaims()
{
var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsWithExtendedClaims, _tokenValidationParameters).ConfigureAwait(false);
var claimsIdentity = result.ClaimsIdentity;
var claims = claimsIdentity.Claims;
return claims.ToList();
}

//[Benchmark, BenchmarkCategory("ValidateAndGetClaims")]
public async Task<IList<Claim>> NewClaimsIdentity_ValidateTokenAndGetClaims()
{
var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsWithExtendedClaims, _newTokenValidationParameters).ConfigureAwait(false);
var claimsIdentity = result.ClaimsIdentity;
var claims = claimsIdentity.Claims;
return claims.ToList();
}
}
}
9 changes: 9 additions & 0 deletions benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ public static void Main(string[] args)
}
private static void DebugThroughTests()
{
ClaimsIdentityTests claimsIdentityTests = new ClaimsIdentityTests();
claimsIdentityTests.SetupAsync().GetAwaiter().GetResult();
var claim = claimsIdentityTests.ClaimsIdentity_FindFirst();
var claimsList = claimsIdentityTests.ClaimsIdentity_FindAll();
var hasClaim = claimsIdentityTests.ClaimsIdentity_HasClaim();
claim = claimsIdentityTests.NewClaimsIdentity_FindFirst();
claimsList = claimsIdentityTests.NewClaimsIdentity_FindAll();
hasClaim = claimsIdentityTests.NewClaimsIdentity_HasClaim();

ReadJWETokenTests readTokenTests = new ReadJWETokenTests();
readTokenTests.Setup();
readTokenTests.ReadJWE_FromMemory();
Expand Down
3 changes: 2 additions & 1 deletion build/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<PropertyGroup>
<NoWarn>$(NoWarn);SYSLIB0050</NoWarn>
<NoWarn>$(NoWarn);SYSLIB0051</NoWarn>
<NoWarn>$(NoWarn);RS0016;RS0017;RS0051</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -77,5 +78,5 @@
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

</Project>
1 change: 1 addition & 0 deletions build/commonTest.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<NoWarn>$(NoWarn);SYSLIB0050</NoWarn>
<NoWarn>$(NoWarn);SYSLIB0051</NoWarn>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);RS0016;RS0017;RS0051</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
80 changes: 78 additions & 2 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,85 @@ internal bool TryGetValue<T>(string key, out T value)
return found;
}

internal bool HasClaim(string claimName)
internal Claim GetPayloadClaim(string name, string issuer)
{
return _jsonClaims.TryGetValue(claimName, out _);
if (_jsonClaims.TryGetValue(name, out object val))
{
Claim claim = CreateClaimFromObject(name, val, issuer);
if (claim != null)
return claim;
}

return null;
}

internal bool HasPayloadClaim(string name)
{
return _jsonClaims.TryGetValue(name, out _);
}

internal bool HasPayloadClaim(string name, string value, string issuer)
{
if (_jsonClaims.TryGetValue(name, out object val))
{
Claim claim = CreateClaimFromObject(name, val, issuer);
if (claim != null)
return claim?.Value.Equals(value, StringComparison.Ordinal) == true;
}

return false;
}

internal static Claim CreateClaimFromObject(string claimType, object value, string issuer)
{
// Json.net recognized DateTime by default.
if (value is string str)
return new Claim(claimType, str, JwtTokenUtilities.GetStringClaimValueType(str, claimType), issuer, issuer);
else if (value is int i)
return new Claim(claimType, i.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer32, issuer, issuer);
else if (value is long l)
return new Claim(claimType, l.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64, issuer, issuer);
else if (value is bool b)
{
// Can't just use ToString or bools will get encoded as True/False instead of true/false.
if (b)
return new Claim(claimType, "true", ClaimValueTypes.Boolean, issuer, issuer);
else
return new Claim(claimType, "false", ClaimValueTypes.Boolean, issuer, issuer);
}
else if (value is double d)
return new Claim(claimType, d.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer);
else if (value is DateTime dt)
return new Claim(claimType, dt.ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer);
else if (value is float f)
return new Claim(claimType, f.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer);
else if (value is decimal m)
return new Claim(claimType, m.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer);
else if (value is null)
return new Claim(claimType, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer);
else if (value is IList ilist)
{
foreach (var item in ilist)
return CreateClaimFromObject(claimType, item, issuer);
}
else if (value is JsonElement j)
if (j.ValueKind == JsonValueKind.Array)
{
foreach (JsonElement jsonElement in j.EnumerateArray())
{
Claim claim = CreateClaimFromJsonElement(claimType, issuer, jsonElement);
if (claim != null)
return claim;
}
}
else
{
Claim claim = CreateClaimFromJsonElement(claimType, issuer, j);
if (claim != null)
return claim;
}

return null;
}
}
}
25 changes: 19 additions & 6 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.IdentityModel.JsonWebTokens
/// <summary>
/// A <see cref="SecurityToken"/> designed for representing a JSON Web Token (JWT).
/// </summary>
public partial class JsonWebToken : SecurityToken
public partial class JsonWebToken : ClaimsProvider
{
internal const string ClassName = "Microsoft.IdentityModel.JsonWebTokens.JsonWebToken";

Expand Down Expand Up @@ -624,6 +624,24 @@ public Claim GetClaim(string key)
return Payload.GetClaim(key, Issuer ?? ClaimsIdentity.DefaultIssuer);
}

/// <inheritdoc/>
public override Claim GetPayloadClaim(string name)
{
return Payload.GetPayloadClaim(name, Issuer ?? ClaimsIdentity.DefaultIssuer);
}

/// <inheritdoc/>
public override bool HasPayloadClaim(string name, string value)
{
return Payload.HasPayloadClaim(name, value, Issuer ?? ClaimsIdentity.DefaultIssuer);
}

/// <inheritdoc/>
public override bool HasPayloadClaim(string type)
{
return Payload.HasPayloadClaim(type);
}

/// <summary>
/// Gets the names of the payload claims on the JsonWebToken.
/// </summary>
Expand Down Expand Up @@ -700,11 +718,6 @@ public bool TryGetClaim(string key, out Claim value)
#endregion

#region Get Claims from the JWT Header and Payload
internal bool HasPayloadClaim(string claimName)
{
return Payload.HasClaim(claimName);
}

/// <summary>
/// Gets the 'value' corresponding to key from the JWT header transformed as type 'T'.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,25 @@ internal static ClaimsIdentity Create(IEnumerable<Claim> claims, string authenti
return new CaseSensitiveClaimsIdentity(claims, authenticationType);
}

internal static ClaimsIdentity Create(string authenticationType, string nameType, string roleType, SecurityToken securityToken)
internal static ClaimsIdentity Create(string authenticationType, string nameType, string roleType, SecurityToken securityToken, TokenValidationParameters tokenValidationParameters)
{
if (AppContextSwitches.UseClaimsIdentityType)
return new ClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType);

return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType)
if (tokenValidationParameters.UseNewClaimsIdentityType)
{
SecurityToken = securityToken,
};
return new SecurityTokenClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType)
{
SecurityToken = securityToken,
};
}
else
{
return new CaseSensitiveClaimsIdentity(authenticationType: authenticationType, nameType: nameType, roleType: roleType)
{
SecurityToken = securityToken,
};
}
}
}

Expand Down
Loading
Loading