Skip to content

Commit 0e2d8a6

Browse files
Better thread safety for JsonClaimSet Claims and JsonWebToken Audiences (#2185)
1 parent 286d780 commit 0e2d8a6

File tree

3 files changed

+77
-49
lines changed

3 files changed

+77
-49
lines changed

src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs

+12-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace Microsoft.IdentityModel.JsonWebTokens
2121
internal class JsonClaimSet
2222
{
2323
private IList<Claim> _claims;
24+
private readonly object _claimsLock = new object();
2425

2526
internal JsonClaimSet(JsonDocument jsonDocument)
2627
{
@@ -41,10 +42,17 @@ internal JsonClaimSet(string json)
4142

4243
internal IList<Claim> Claims(string issuer)
4344
{
44-
if (_claims != null)
45-
return _claims;
46-
47-
_claims = CreateClaims(issuer);
45+
if (_claims == null)
46+
{
47+
lock (_claimsLock)
48+
{
49+
if (_claims == null)
50+
{
51+
_claims = CreateClaims(issuer);
52+
}
53+
}
54+
}
55+
4856
return _claims;
4957
}
5058

src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet45.cs

+39-27
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace Microsoft.IdentityModel.JsonWebTokens
2121
internal class JsonClaimSet45
2222
{
2323
IList<Claim> _claims;
24+
private readonly object _claimsLock = new object();
2425

2526
internal JsonClaimSet45()
2627
{
@@ -122,35 +123,46 @@ internal IList<Claim> CreateClaims(string issuer)
122123

123124
internal IList<Claim> Claims(string issuer)
124125
{
125-
if (_claims != null)
126-
return _claims;
127-
128-
_claims = new List<Claim>();
129-
130-
if (!RootElement.HasValues)
131-
return _claims;
132-
133-
// 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.
134-
foreach (var entry in RootElement)
126+
if (_claims == null)
135127
{
136-
if (entry.Value == null)
137-
{
138-
_claims.Add(new Claim(entry.Key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer));
139-
continue;
140-
}
141-
142-
if (entry.Value.Type is JTokenType.String)
128+
lock (_claimsLock)
143129
{
144-
var claimValue = entry.Value.ToObject<string>();
145-
_claims.Add(new Claim(entry.Key, claimValue, ClaimValueTypes.String, issuer, issuer));
146-
continue;
147-
}
148-
149-
var jtoken = entry.Value;
150-
if (jtoken != null)
151-
{
152-
AddClaimsFromJToken(_claims, entry.Key, jtoken, issuer);
153-
continue;
130+
if (_claims == null)
131+
{
132+
var claims = new List<Claim>();
133+
134+
if (!RootElement.HasValues)
135+
{
136+
_claims = claims;
137+
return _claims;
138+
}
139+
140+
// 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.
141+
foreach (var entry in RootElement)
142+
{
143+
if (entry.Value == null)
144+
{
145+
claims.Add(new Claim(entry.Key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer));
146+
continue;
147+
}
148+
149+
if (entry.Value.Type is JTokenType.String)
150+
{
151+
var claimValue = entry.Value.ToObject<string>();
152+
claims.Add(new Claim(entry.Key, claimValue, ClaimValueTypes.String, issuer, issuer));
153+
continue;
154+
}
155+
156+
var jtoken = entry.Value;
157+
if (jtoken != null)
158+
{
159+
AddClaimsFromJToken(claims, entry.Key, jtoken, issuer);
160+
continue;
161+
}
162+
}
163+
164+
_claims = claims;
165+
}
154166
}
155167
}
156168

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs

+26-18
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class JsonWebToken : SecurityToken
3737
private string _act;
3838
private string _alg;
3939
private IList<string> _audiences;
40+
private readonly object _audiencesLock = new object();
4041
private string _authenticationTag;
4142
private string _ciphertext;
4243
private string _cty;
@@ -650,28 +651,35 @@ public IEnumerable<string> Audiences
650651
{
651652
if (_audiences == null)
652653
{
653-
_audiences = new List<string>();
654-
#if NET45
655-
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JToken value))
656-
{
657-
if (value.Type is JTokenType.String)
658-
_audiences = new List<string> { value.ToObject<string>() };
659-
else if (value.Type is JTokenType.Array)
660-
_audiences = value.ToObject<List<string>>();
661-
}
662-
#else
663-
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JsonElement audiences))
654+
lock (_audiencesLock)
664655
{
665-
if (audiences.ValueKind == JsonValueKind.String)
666-
_audiences = new List<string> { audiences.GetString() };
667-
668-
if (audiences.ValueKind == JsonValueKind.Array)
656+
if (_audiences == null)
669657
{
670-
foreach (JsonElement jsonElement in audiences.EnumerateArray())
671-
_audiences.Add(jsonElement.ToString());
658+
var aud = new List<string>();
659+
#if NET45
660+
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JToken value))
661+
{
662+
if (value.Type is JTokenType.String)
663+
aud = new List<string> { value.ToObject<string>() };
664+
else if (value.Type is JTokenType.Array)
665+
aud = value.ToObject<List<string>>();
666+
}
667+
#else
668+
if (Payload.TryGetValue(JwtRegisteredClaimNames.Aud, out JsonElement audiences))
669+
{
670+
if (audiences.ValueKind == JsonValueKind.String)
671+
aud = new List<string> { audiences.GetString() };
672+
673+
if (audiences.ValueKind == JsonValueKind.Array)
674+
{
675+
foreach (JsonElement jsonElement in audiences.EnumerateArray())
676+
aud.Add(jsonElement.ToString());
677+
}
678+
}
679+
#endif
680+
_audiences = aud;
672681
}
673682
}
674-
#endif
675683
}
676684

677685
return _audiences;

0 commit comments

Comments
 (0)