Skip to content

Commit

Permalink
Better thread safety for JsonClaimSet Claims and JsonWebToken Audienc…
Browse files Browse the repository at this point in the history
…es (#2185)
  • Loading branch information
keegan-caruso committed Jul 27, 2023
1 parent 286d780 commit 0e2d8a6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 49 deletions.
16 changes: 12 additions & 4 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Microsoft.IdentityModel.JsonWebTokens
internal class JsonClaimSet
{
private IList<Claim> _claims;
private readonly object _claimsLock = new object();

internal JsonClaimSet(JsonDocument jsonDocument)
{
Expand All @@ -41,10 +42,17 @@ internal JsonClaimSet(string json)

internal IList<Claim> Claims(string issuer)
{
if (_claims != null)
return _claims;

_claims = CreateClaims(issuer);
if (_claims == null)
{
lock (_claimsLock)
{
if (_claims == null)
{
_claims = CreateClaims(issuer);
}
}
}

return _claims;
}

Expand Down
66 changes: 39 additions & 27 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet45.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Microsoft.IdentityModel.JsonWebTokens
internal class JsonClaimSet45
{
IList<Claim> _claims;
private readonly object _claimsLock = new object();

internal JsonClaimSet45()
{
Expand Down Expand Up @@ -122,35 +123,46 @@ internal IList<Claim> CreateClaims(string issuer)

internal IList<Claim> Claims(string issuer)
{
if (_claims != null)
return _claims;

_claims = new List<Claim>();

if (!RootElement.HasValues)
return _claims;

// there is some code redundancy here that was not factored as this is a high use method. Each identity received from the host will pass through here.
foreach (var entry in RootElement)
if (_claims == null)
{
if (entry.Value == null)
{
_claims.Add(new Claim(entry.Key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer));
continue;
}

if (entry.Value.Type is JTokenType.String)
lock (_claimsLock)
{
var claimValue = entry.Value.ToObject<string>();
_claims.Add(new Claim(entry.Key, claimValue, ClaimValueTypes.String, issuer, issuer));
continue;
}

var jtoken = entry.Value;
if (jtoken != null)
{
AddClaimsFromJToken(_claims, entry.Key, jtoken, issuer);
continue;
if (_claims == null)
{
var claims = new List<Claim>();

if (!RootElement.HasValues)
{
_claims = claims;
return _claims;
}

// there is some code redundancy here that was not factored as this is a high use method. Each identity received from the host will pass through here.
foreach (var entry in RootElement)
{
if (entry.Value == null)
{
claims.Add(new Claim(entry.Key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer));
continue;
}

if (entry.Value.Type is JTokenType.String)
{
var claimValue = entry.Value.ToObject<string>();
claims.Add(new Claim(entry.Key, claimValue, ClaimValueTypes.String, issuer, issuer));
continue;
}

var jtoken = entry.Value;
if (jtoken != null)
{
AddClaimsFromJToken(claims, entry.Key, jtoken, issuer);
continue;
}
}

_claims = claims;
}
}
}

Expand Down
44 changes: 26 additions & 18 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class JsonWebToken : SecurityToken
private string _act;
private string _alg;
private IList<string> _audiences;
private readonly object _audiencesLock = new object();
private string _authenticationTag;
private string _ciphertext;
private string _cty;
Expand Down Expand Up @@ -650,28 +651,35 @@ public IEnumerable<string> Audiences
{
if (_audiences == null)
{
_audiences = new List<string>();
#if NET45
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JToken value))
{
if (value.Type is JTokenType.String)
_audiences = new List<string> { value.ToObject<string>() };
else if (value.Type is JTokenType.Array)
_audiences = value.ToObject<List<string>>();
}
#else
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JsonElement audiences))
lock (_audiencesLock)
{
if (audiences.ValueKind == JsonValueKind.String)
_audiences = new List<string> { audiences.GetString() };

if (audiences.ValueKind == JsonValueKind.Array)
if (_audiences == null)
{
foreach (JsonElement jsonElement in audiences.EnumerateArray())
_audiences.Add(jsonElement.ToString());
var aud = new List<string>();
#if NET45
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JToken value))
{
if (value.Type is JTokenType.String)
aud = new List<string> { value.ToObject<string>() };
else if (value.Type is JTokenType.Array)
aud = value.ToObject<List<string>>();
}
#else
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JsonElement audiences))
{
if (audiences.ValueKind == JsonValueKind.String)
aud = new List<string> { audiences.GetString() };

if (audiences.ValueKind == JsonValueKind.Array)
{
foreach (JsonElement jsonElement in audiences.EnumerateArray())
aud.Add(jsonElement.ToString());
}
}
#endif
_audiences = aud;
}
}
#endif
}

return _audiences;
Expand Down

0 comments on commit 0e2d8a6

Please sign in to comment.