diff --git a/build/common.props b/build/common.props
index 000c4f5136..001d7daab7 100644
--- a/build/common.props
+++ b/build/common.props
@@ -48,8 +48,4 @@
-
-
-
-
diff --git a/build/commonTest.props b/build/commonTest.props
index ee3387d6d9..bdbdb21cfc 100644
--- a/build/commonTest.props
+++ b/build/commonTest.props
@@ -17,6 +17,7 @@
$(TestOnlyCoreTargets)
$(DotNetCoreAppRuntimeVersion)
false
+ 11
diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs
index d916f9995e..aa1485358a 100644
--- a/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs
+++ b/src/Microsoft.IdentityModel.JsonWebTokens/GlobalSuppressions.cs
@@ -34,7 +34,7 @@
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenAsync(System.String,Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters)~System.Threading.Tasks.Task{Microsoft.IdentityModel.Tokens.TokenValidationResult}")]
[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Vendored component", Scope = "module")]
[assembly: SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "It is used within a defined if condition", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.GetSecurityKey(Microsoft.IdentityModel.Tokens.EncryptingCredentials,Microsoft.IdentityModel.Tokens.CryptoProviderFactory,System.Collections.Generic.IDictionary{System.String,System.Object},System.Byte[]@)~Microsoft.IdentityModel.Tokens.SecurityKey")]
-[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateTokenPrivate(System.String,Microsoft.IdentityModel.Tokens.SigningCredentials,Microsoft.IdentityModel.Tokens.EncryptingCredentials,System.String,System.Collections.Generic.IDictionary{System.String,System.Object},System.Collections.Generic.IDictionary{System.String,System.Object},System.String)~System.String")]
+[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateToken(System.String,Microsoft.IdentityModel.Tokens.SigningCredentials,Microsoft.IdentityModel.Tokens.EncryptingCredentials,System.String,System.Collections.Generic.IDictionary{System.String,System.Object},System.Collections.Generic.IDictionary{System.String,System.Object},System.String)~System.String")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateSignature(System.String,Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~Microsoft.IdentityModel.JsonWebTokens.JsonWebToken")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "There are additional keys to check, the next one may be successful", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateSignature(Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~Microsoft.IdentityModel.JsonWebTokens.JsonWebToken")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exception is written to a string", Scope = "member", Target = "~M:Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.GetContentEncryptionKeys(Microsoft.IdentityModel.JsonWebTokens.JsonWebToken,Microsoft.IdentityModel.Tokens.TokenValidationParameters,Microsoft.IdentityModel.Tokens.BaseConfiguration)~System.Collections.Generic.IEnumerable{Microsoft.IdentityModel.Tokens.SecurityKey}")]
diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
index cb8bf87678..67de58bc3c 100644
--- a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
+++ b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
@@ -2,13 +2,14 @@
// Licensed under the MIT License.
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Claims;
+using System.Text;
using System.Text.Json;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json.Linq;
namespace Microsoft.IdentityModel.JsonWebTokens
{
@@ -18,121 +19,134 @@ namespace Microsoft.IdentityModel.JsonWebTokens
///
internal class JsonClaimSet
{
- internal static JsonClaimSet Empty { get; } = new JsonClaimSet("{}"u8.ToArray());
+ internal const string ClassName = "Microsoft.IdentityModel.JsonWebTokens.JsonClaimSet";
+ internal static JsonClaimSet Empty { get; } = new JsonClaimSet("{}"u8.ToArray());
+ internal object _claimsLock = new();
+ internal IDictionary _jsonClaims;
private IList _claims;
- internal JsonClaimSet(JsonDocument jsonDocument)
+ internal JsonClaimSet(IDictionary jsonClaims)
{
- // This method is assuming ownership of the JsonDocument, which is backed by one or more ArrayPool arrays.
- // We need to dispose of it to avoid leaking arrays from the pool. To achieve that, we clone the root element,
- // which will result in a new JsonElement being created that's not tied to the original and that's not backed by
- // ArrayPool memory, after which point we can dispose of the original to return the array(s) to the pool.
- RootElement = jsonDocument.RootElement.Clone();
- jsonDocument.Dispose();
+ _jsonClaims = jsonClaims;
}
-
- internal JsonClaimSet(byte[] jsonBytes) : this(JsonDocument.Parse(jsonBytes))
+ internal JsonClaimSet(byte[] jsonUtf8Bytes)
{
+ _jsonClaims = JwtTokenUtilities.CreateClaimsDictionary(jsonUtf8Bytes, jsonUtf8Bytes.Length);
}
- internal JsonClaimSet(string json) : this(JsonDocument.Parse(json))
+ internal JsonClaimSet(string json) : this(Encoding.UTF8.GetBytes(json))
{
}
- internal JsonElement RootElement { get; }
-
internal IList Claims(string issuer)
{
- if (_claims != null)
- return _claims;
+ if (_claims == null)
+ lock (_claimsLock)
+ _claims ??= CreateClaims(issuer);
- _claims = CreateClaims(issuer);
return _claims;
}
internal IList CreateClaims(string issuer)
{
IList claims = new List();
- foreach (JsonProperty property in RootElement.EnumerateObject())
+ foreach (KeyValuePair kvp in _jsonClaims)
+ CreateClaimFromObject(claims, kvp.Key, kvp.Value, issuer);
+
+ return claims;
+ }
+
+ internal static void CreateClaimFromObject(IList claims, string claimType, object value, string issuer)
+ {
+ // Json.net recognized DateTime by default.
+ if (value is string str)
+ claims.Add(new Claim(claimType, str, JwtTokenUtilities.GetStringClaimValueType(str), issuer, issuer));
+ else if (value is int i)
+ claims.Add(new Claim(claimType, i.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer32, issuer, issuer));
+ 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));
+ 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)
+ claims.Add(new Claim(claimType, dt.ToString("o",CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer));
+ else if (value is float f)
+ claims.Add(new Claim(claimType, f.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer));
+ else if (value is decimal m)
+ claims.Add(new Claim(claimType, m.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer));
+ else if (value is null)
+ claims.Add(new Claim(claimType, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer));
+ else if (value is IList ilist)
{
- if (property.Value.ValueKind == JsonValueKind.Array)
+ foreach (var item in ilist)
+ CreateClaimFromObject(claims, claimType, item, issuer);
+ }
+ else if (value is JsonElement j)
+ if (j.ValueKind == JsonValueKind.Array)
{
- foreach (JsonElement jsonElement in property.Value.EnumerateArray())
+ foreach (JsonElement jsonElement in j.EnumerateArray())
{
- Claim claim = CreateClaimFromJsonElement(property.Name, issuer, jsonElement);
+ Claim claim = CreateClaimFromJsonElement(claimType, issuer, jsonElement);
if (claim != null)
claims.Add(claim);
}
}
else
{
- Claim claim = CreateClaimFromJsonElement(property.Name, issuer, property.Value);
+ Claim claim = CreateClaimFromJsonElement(claimType, issuer, j);
if (claim != null)
claims.Add(claim);
}
- }
-
- return claims;
}
- private static Claim CreateClaimFromJsonElement(string key, string issuer, JsonElement jsonElement)
+ internal static Claim CreateClaimFromJsonElement(string claimType, string issuer, JsonElement value)
{
// Json.net recognized DateTime by default.
- if (jsonElement.ValueKind == JsonValueKind.String)
+ if (value.ValueKind == JsonValueKind.String)
{
- try
- {
- if (jsonElement.TryGetDateTime(out DateTime dateTimeValue))
- return new Claim(key, dateTimeValue.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer);
- else
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.String, issuer, issuer);
- }
- catch(IndexOutOfRangeException)
- {
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.String, issuer, issuer);
- }
+ string claimValue = value.ToString();
+ return new Claim(claimType, claimValue, JwtTokenUtilities.GetStringClaimValueType(claimValue), issuer, issuer);
}
- else if (jsonElement.ValueKind == JsonValueKind.Null)
- return new Claim(key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer);
- else if (jsonElement.ValueKind == JsonValueKind.Object)
- return new Claim(key, jsonElement.ToString(), JsonClaimValueTypes.Json, issuer, issuer);
- else if (jsonElement.ValueKind == JsonValueKind.False)
- return new Claim(key, "false", ClaimValueTypes.Boolean, issuer, issuer);
- else if (jsonElement.ValueKind == JsonValueKind.True)
- return new Claim(key, "true", ClaimValueTypes.Boolean, issuer, issuer);
- else if (jsonElement.ValueKind == JsonValueKind.Number)
+ else if (value.ValueKind == JsonValueKind.Null)
+ return new Claim(claimType, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer);
+ else if (value.ValueKind == JsonValueKind.Object)
+ return new Claim(claimType, value.ToString(), JsonClaimValueTypes.Json, issuer, issuer);
+ else if (value.ValueKind == JsonValueKind.False)
+ return new Claim(claimType, "False", ClaimValueTypes.Boolean, issuer, issuer);
+ else if (value.ValueKind == JsonValueKind.True)
+ return new Claim(claimType, "True", ClaimValueTypes.Boolean, issuer, issuer);
+ else if (value.ValueKind == JsonValueKind.Number)
{
- if (jsonElement.TryGetInt16(out short _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.Integer, issuer, issuer);
- else if (jsonElement.TryGetInt32(out int _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.Integer, issuer, issuer);
- else if (jsonElement.TryGetInt64(out long _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.Integer64, issuer, issuer);
- else if (jsonElement.TryGetDecimal(out decimal _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.Double, issuer, issuer);
- else if (jsonElement.TryGetDouble(out double _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.Double, issuer, issuer);
- else if (jsonElement.TryGetUInt32(out uint _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.UInteger32, issuer, issuer);
- else if (jsonElement.TryGetUInt64(out ulong _))
- return new Claim(key, jsonElement.ToString(), ClaimValueTypes.UInteger64, issuer, issuer);
+ if (value.TryGetInt32(out int i))
+ return new Claim(claimType, i.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer, issuer, issuer);
+ else if (value.TryGetInt64(out long l))
+ return new Claim(claimType, l.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64, issuer, issuer);
+ else if (value.TryGetUInt32(out uint u))
+ return new Claim(claimType, u.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.UInteger32, issuer, issuer);
+ else if (value.TryGetDouble(out double d))
+ return new Claim(claimType, d.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer);
+ else if (value.TryGetDecimal(out decimal m))
+ return new Claim(claimType, m.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Double, issuer, issuer);
+ else if (value.TryGetUInt64(out ulong ul))
+ return new Claim(claimType, ul.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.UInteger64, issuer, issuer);
}
- else if (jsonElement.ValueKind == JsonValueKind.Array)
+ else if (value.ValueKind == JsonValueKind.Array)
{
- return new Claim(key, jsonElement.ToString(), JsonClaimValueTypes.JsonArray, issuer, issuer);
+ return new Claim(claimType, value.ToString(), JsonClaimValueTypes.JsonArray, issuer, issuer);
}
return null;
}
- private static object CreateObjectFromJsonElement(JsonElement jsonElement)
+ internal static object CreateObjectFromJsonElement(JsonElement jsonElement)
{
if (jsonElement.ValueKind == JsonValueKind.Array)
{
int numberOfElements = 0;
// is this an array of properties
- foreach(JsonElement element in jsonElement.EnumerateArray())
+ foreach (JsonElement element in jsonElement.EnumerateArray())
numberOfElements++;
object[] objects = new object[numberOfElements];
@@ -180,67 +194,38 @@ private static object CreateObjectFromJsonElement(JsonElement jsonElement)
internal Claim GetClaim(string key, string issuer)
{
if (key == null)
- throw new ArgumentNullException(nameof(key));
-
- if (!RootElement.TryGetProperty(key, out JsonElement jsonElement))
- throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX14304, key)));
-
- return CreateClaimFromJsonElement(key, issuer, jsonElement);
- }
-
- internal static string GetClaimValueType(object obj)
- {
- if (obj == null)
- return JsonClaimValueTypes.JsonNull;
-
- var objType = obj.GetType();
-
- if (objType == typeof(string))
- return ClaimValueTypes.String;
-
- if (objType == typeof(int))
- return ClaimValueTypes.Integer;
-
- if (objType == typeof(bool))
- return ClaimValueTypes.Boolean;
-
- if (objType == typeof(double))
- return ClaimValueTypes.Double;
+ throw LogHelper.LogArgumentNullException(nameof(key));
- if (objType == typeof(long))
+ if (_jsonClaims.TryGetValue(key, out object _))
{
- long l = (long)obj;
- if (l >= int.MinValue && l <= int.MaxValue)
- return ClaimValueTypes.Integer;
-
- return ClaimValueTypes.Integer64;
+ foreach (var claim in Claims(issuer))
+ if (claim.Type == key)
+ return claim;
}
- if (objType == typeof(DateTime))
- return ClaimValueTypes.DateTime;
-
- return objType.ToString();
+ throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX14304, key)));
}
internal string GetStringValue(string key)
{
- if (RootElement.TryGetProperty(key, out JsonElement jsonElement) && jsonElement.ValueKind == JsonValueKind.String)
- return jsonElement.GetString();
+ if (_jsonClaims.TryGetValue(key, out object obj))
+ return obj.ToString();
return string.Empty;
}
internal DateTime GetDateTime(string key)
{
- if (!RootElement.TryGetProperty(key, out JsonElement jsonElement))
+ if (!_jsonClaims.TryGetValue(key, out object value))
return DateTime.MinValue;
- return EpochTime.DateTime(Convert.ToInt64(Math.Truncate((double)ParseTimeValue(key, jsonElement))));
+ return EpochTime.DateTime(Convert.ToInt64(Math.Truncate((double)GetValueAsLong(key, value))));
}
internal T GetValue(string key)
{
- return GetValue(key, true, out bool _);
+ T retval = GetValue(key, true, out bool _);
+ return retval;
}
///
@@ -256,7 +241,8 @@ internal T GetValue(string key)
///
internal T GetValue(string key, bool throwEx, out bool found)
{
- found = RootElement.TryGetProperty(key, out JsonElement jsonElement);
+ found = _jsonClaims.TryGetValue(key, out object obj);
+
if (!found)
{
if (throwEx)
@@ -265,104 +251,145 @@ internal T GetValue(string key, bool throwEx, out bool found)
return default;
}
- if (typeof(T) == typeof(JsonElement))
- return (T)(object)jsonElement;
+ if (obj == null)
+ if (typeof(T) == typeof(object) || typeof(T).IsClass || Nullable.GetUnderlyingType(typeof(T)) != null)
+ {
+ return (T)(object)null;
+ }
+ else
+ {
+ found = false;
+ return default;
+ }
+
+ Type objType = obj.GetType();
+
+ if (typeof(T) == objType)
+ return (T)(obj);
+
+ if (typeof(T) == typeof(object))
+ return (T)obj;
- try
+ if (typeof(T) == typeof(string))
+ return (T)((object)obj.ToString());
+
+ if (typeof(T) == typeof(IList))
{
- if (jsonElement.ValueKind == JsonValueKind.Null)
+ List list = new();
+ if (obj is IList iList)
{
- if (typeof(T) == typeof(object) || typeof(T).IsClass || Nullable.GetUnderlyingType(typeof(T)) != null)
- return (T)(object)null;
- else
- {
- found = false;
- return default;
- }
+ foreach (object item in iList)
+ list.Add(item?.ToString());
+
+ return (T)((object)list);
}
else
{
- if (typeof(T) == typeof(JObject))
- return (T)(object)(JObject.Parse(jsonElement.ToString()));
+ list.Add(obj.ToString());
+ }
+ return (T)(object)list;
+ }
- if (typeof(T) == typeof(JArray))
- return (T)(object)(JArray.Parse(jsonElement.ToString()));
+ if (typeof(T) == typeof(int) && int.TryParse(obj.ToString(), out int i))
+ return (T)(object)i;
- if (typeof(T) == typeof(object))
- return (T)CreateObjectFromJsonElement(jsonElement);
+ if (typeof(T) == typeof(long) && long.TryParse(obj.ToString(), out long l))
+ return (T)(object)l;
- if (typeof(T) == typeof(object[]))
- {
- if (jsonElement.ValueKind == JsonValueKind.Array)
- {
- int numberOfElements = 0;
- // is this an array of properties
- foreach (JsonElement element in jsonElement.EnumerateArray())
- numberOfElements++;
-
- object[] objects = new object[numberOfElements];
-
- int index = 0;
- foreach (JsonElement element in jsonElement.EnumerateArray())
- objects[index++] = CreateObjectFromJsonElement(element);
-
- return (T)(object)objects;
- }
- else
- {
- object[] objects = new object[1];
- objects[0] = CreateObjectFromJsonElement(jsonElement);
- return (T)(object)objects;
- }
- }
+ if (typeof(T) == typeof(double) && double.TryParse(obj.ToString(), out double d))
+ return (T)(object)d;
- if (typeof(T) == typeof(string))
- return (T)(jsonElement.ToString() as object);
+ if (typeof(T) == typeof(DateTime) && DateTime.TryParse(obj.ToString(), out DateTime dt))
+ return (T)(object)dt;
- if (jsonElement.ValueKind == JsonValueKind.String)
- {
- if (typeof(T) == typeof(long) && long.TryParse(jsonElement.ToString(), out long lresult))
- return (T)(object)lresult;
+ if (typeof(T) == typeof(uint) && uint.TryParse(obj.ToString(), out uint u))
+ return (T)(object)u;
- if (typeof(T) == typeof(int) && int.TryParse(jsonElement.ToString(), out int iresult))
- return (T)(object)iresult;
+ if (typeof(T) == typeof(float) && float.TryParse(obj.ToString(), out float f))
+ return (T)(object)f;
- if (typeof(T) == typeof(DateTime))
- if (DateTime.TryParse(jsonElement.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime dateTime))
- return (T)(object)dateTime;
- else
- return JsonSerializer.Deserialize(jsonElement.GetRawText());
+ if (typeof(T) == typeof(decimal) && decimal.TryParse(obj.ToString(), out decimal m))
+ return (T)(object)m;
- if (typeof(T) == typeof(double) && double.TryParse(jsonElement.ToString(), out double dresult))
- return (T)(object)dresult;
+ if (typeof(T) == typeof(IList