diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs
index e8b8f76940..e3daea25e8 100644
--- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs
+++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs
@@ -85,6 +85,7 @@ public static string CreateEncodedSignature(string input, SigningCredentials sig
/// or is null.
public static string CreateEncodedSignature(string input, SigningCredentials signingCredentials, bool cacheProvider)
{
+ // TODO create overload that takes a Span for the input
if (input == null)
throw LogHelper.LogArgumentNullException(nameof(input));
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
index 753c5dbd26..667494c70a 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs
@@ -5,20 +5,47 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
+using System.Text.Json.Serialization;
+using System.Threading;
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json;
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
{
///
/// Contains OpenIdConnect configuration that can be populated from a json string.
///
- [JsonObject]
public class OpenIdConnectConfiguration : BaseConfiguration
{
- private const string _className = "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration";
+ internal const string ClassName = "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration";
+
+ // these are used to lazy create
+ private Dictionary _additionalData;
+ private ICollection _acrValuesSupported;
+ private ICollection _claimsSupported;
+ private ICollection _claimsLocalesSupported;
+ private ICollection _claimTypesSupported;
+ private ICollection _displayValuesSupported;
+ private ICollection _grantTypesSupported;
+ private ICollection _idTokenEncryptionAlgValuesSupported;
+ private ICollection _idTokenEncryptionEncValuesSupported;
+ private ICollection _idTokenSigningAlgValuesSupported;
+ private ICollection _introspectionEndpointAuthMethodsSupported;
+ private ICollection _introspectionEndpointAuthSigningAlgValuesSupported;
+ private ICollection _requestObjectEncryptionAlgValuesSupported;
+ private ICollection _requestObjectEncryptionEncValuesSupported;
+ private ICollection _requestObjectSigningAlgValuesSupported;
+ private ICollection _responseModesSupported;
+ private ICollection _responseTypesSupported;
+ private ICollection _scopesSupported;
+ private ICollection _subjectTypesSupported;
+ private ICollection _tokenEndpointAuthMethodsSupported;
+ private ICollection _tokenEndpointAuthSigningAlgValuesSupported;
+ private ICollection _uILocalesSupported;
+ private ICollection _userInfoEndpointEncryptionAlgValuesSupported;
+ private ICollection _userInfoEndpointEncryptionEncValuesSupported;
+ private ICollection _userInfoEndpointSigningAlgValuesSupported;
///
/// Deserializes the json string into an object.
@@ -49,8 +76,10 @@ public static string Write(OpenIdConnectConfiguration configuration)
if (configuration == null)
throw LogHelper.LogArgumentNullException(nameof(configuration));
- LogHelper.LogVerbose(LogMessages.IDX21809);
- return JsonConvert.SerializeObject(configuration);
+ if (LogHelper.IsEnabled(EventLogLevel.Verbose))
+ LogHelper.LogVerbose(LogMessages.IDX21809);
+
+ return OpenIdConnectConfigurationSerializer.Write(configuration);
}
///
@@ -67,19 +96,19 @@ public OpenIdConnectConfiguration()
/// If 'json' is null or empty.
public OpenIdConnectConfiguration(string json)
{
- if(string.IsNullOrEmpty(json))
+ if (string.IsNullOrEmpty(json))
throw LogHelper.LogArgumentNullException(nameof(json));
try
{
if (LogHelper.IsEnabled(EventLogLevel.Verbose))
- LogHelper.LogVerbose(LogMessages.IDX21806, json, LogHelper.MarkAsNonPII(_className));
+ LogHelper.LogVerbose(LogMessages.IDX21806, json, LogHelper.MarkAsNonPII(ClassName));
- JsonConvert.PopulateObject(json, this);
+ OpenIdConnectConfigurationSerializer.Read(json, this);
}
catch (Exception ex)
{
- throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX21815, json, LogHelper.MarkAsNonPII(_className)), ex));
+ throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX21815, json, LogHelper.MarkAsNonPII(ClassName)), ex));
}
}
@@ -87,222 +116,330 @@ public OpenIdConnectConfiguration(string json)
/// When deserializing from JSON any properties that are not defined will be placed here.
///
[JsonExtensionData]
- public virtual IDictionary AdditionalData { get; } = new Dictionary();
+ public IDictionary AdditionalData =>
+ _additionalData ??
+ Interlocked.CompareExchange(ref _additionalData, new Dictionary(StringComparer.Ordinal), null) ??
+ _additionalData;
///
/// Gets the collection of 'acr_values_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.AcrValuesSupported, Required = Required.Default)]
- public ICollection AcrValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.AcrValuesSupported)]
+ public ICollection AcrValuesSupported =>
+ _acrValuesSupported ??
+ Interlocked.CompareExchange(ref _acrValuesSupported, new Collection(), null) ??
+ _acrValuesSupported;
///
/// Gets or sets the 'authorization_endpoint'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.AuthorizationEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.AuthorizationEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string AuthorizationEndpoint { get; set; }
///
/// Gets or sets the 'check_session_iframe'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.CheckSessionIframe, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.CheckSessionIframe)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string CheckSessionIframe { get; set; }
///
/// Gets the collection of 'claims_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ClaimsSupported, Required = Required.Default)]
- public ICollection ClaimsSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ClaimsSupported)]
+ public ICollection ClaimsSupported =>
+ _claimsSupported ??
+ Interlocked.CompareExchange(ref _claimsSupported, new Collection(), null) ??
+ _claimsSupported;
///
/// Gets the collection of 'claims_locales_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ClaimsLocalesSupported, Required = Required.Default)]
- public ICollection ClaimsLocalesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ClaimsLocalesSupported)]
+ public ICollection ClaimsLocalesSupported =>
+ _claimsLocalesSupported ??
+ Interlocked.CompareExchange(ref _claimsLocalesSupported, new Collection(), null) ??
+ _claimsLocalesSupported;
///
/// Gets or sets the 'claims_parameter_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ClaimsParameterSupported, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.ClaimsParameterSupported)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public bool ClaimsParameterSupported { get; set; }
///
/// Gets the collection of 'claim_types_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ClaimTypesSupported, Required = Required.Default)]
- public ICollection ClaimTypesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ClaimsSupported)]
+ public ICollection ClaimTypesSupported =>
+ _claimTypesSupported ??
+ Interlocked.CompareExchange(ref _claimTypesSupported, new Collection(), null) ??
+ _claimTypesSupported;
///
/// Gets the collection of 'display_values_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.DisplayValuesSupported, Required = Required.Default)]
- public ICollection DisplayValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.DisplayValuesSupported)]
+ public ICollection DisplayValuesSupported =>
+ _displayValuesSupported ??
+ Interlocked.CompareExchange(ref _displayValuesSupported, new Collection(), null) ??
+ _displayValuesSupported;
///
/// Gets or sets the 'end_session_endpoint'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.EndSessionEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.EndSessionEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string EndSessionEndpoint { get; set; }
///
/// Gets or sets the 'frontchannel_logout_session_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.FrontchannelLogoutSessionSupported, Required = Required.Default)]
+ /// Would be breaking to change, in 6x it was string, spec says bool.
+ /// TODO - add another property, obsolete and drop in 8x?
+ /// see: https://openid.net/specs/openid-connect-frontchannel-1_0.html
+ ///
+ [JsonPropertyName(OpenIdProviderMetadataNames.FrontchannelLogoutSessionSupported)]
public string FrontchannelLogoutSessionSupported { get; set; }
///
/// Gets or sets the 'frontchannel_logout_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.FrontchannelLogoutSupported, Required = Required.Default)]
+ /// Would be breaking to change, in 6x it was string, spec says bool.
+ /// TODO - add another property, obsolete and drop in 8x?
+ /// see: https://openid.net/specs/openid-connect-frontchannel-1_0.html
+ ///
+ [JsonPropertyName(OpenIdProviderMetadataNames.FrontchannelLogoutSupported)]
public string FrontchannelLogoutSupported { get; set; }
///
/// Gets the collection of 'grant_types_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.GrantTypesSupported, Required = Required.Default)]
- public ICollection GrantTypesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.GrantTypesSupported)]
+ public ICollection GrantTypesSupported =>
+ _grantTypesSupported ??
+ Interlocked.CompareExchange(ref _grantTypesSupported, new Collection(), null) ??
+ _grantTypesSupported;
///
/// Boolean value specifying whether the OP supports HTTP-based logout. Default is false.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.HttpLogoutSupported, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.HttpLogoutSupported)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public bool HttpLogoutSupported { get; set; }
///
/// Gets the collection of 'id_token_encryption_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IdTokenEncryptionAlgValuesSupported, Required = Required.Default)]
- public ICollection IdTokenEncryptionAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.IdTokenEncryptionAlgValuesSupported)]
+ public ICollection IdTokenEncryptionAlgValuesSupported =>
+ _idTokenEncryptionAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _idTokenEncryptionAlgValuesSupported, new Collection(), null) ??
+ _idTokenEncryptionAlgValuesSupported;
///
/// Gets the collection of 'id_token_encryption_enc_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IdTokenEncryptionEncValuesSupported, Required = Required.Default)]
- public ICollection IdTokenEncryptionEncValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.IdTokenEncryptionEncValuesSupported)]
+ public ICollection IdTokenEncryptionEncValuesSupported =>
+ _idTokenEncryptionEncValuesSupported ??
+ Interlocked.CompareExchange(ref _idTokenEncryptionEncValuesSupported, new Collection(), null) ??
+ _idTokenEncryptionEncValuesSupported;
///
/// Gets the collection of 'id_token_signing_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IdTokenSigningAlgValuesSupported, Required = Required.Default)]
- public ICollection IdTokenSigningAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.IdTokenSigningAlgValuesSupported)]
+ public ICollection IdTokenSigningAlgValuesSupported =>
+ _idTokenSigningAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _idTokenSigningAlgValuesSupported, new Collection(), null) ??
+ _idTokenSigningAlgValuesSupported;
///
/// Gets or sets the 'introspection_endpoint'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IntrospectionEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.IntrospectionEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string IntrospectionEndpoint { get; set; }
///
/// Gets the collection of 'introspection_endpoint_auth_methods_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IntrospectionEndpointAuthMethodsSupported, Required = Required.Default)]
- public ICollection IntrospectionEndpointAuthMethodsSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.IntrospectionEndpointAuthMethodsSupported)]
+ public ICollection IntrospectionEndpointAuthMethodsSupported =>
+ _introspectionEndpointAuthMethodsSupported ??
+ Interlocked.CompareExchange(ref _introspectionEndpointAuthMethodsSupported, new Collection(), null) ??
+ _introspectionEndpointAuthMethodsSupported;
///
/// Gets the collection of 'introspection_endpoint_auth_signing_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.IntrospectionEndpointAuthSigningAlgValuesSupported, Required = Required.Default)]
- public ICollection IntrospectionEndpointAuthSigningAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.IntrospectionEndpointAuthSigningAlgValuesSupported)]
+ public ICollection IntrospectionEndpointAuthSigningAlgValuesSupported =>
+ _introspectionEndpointAuthSigningAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _introspectionEndpointAuthSigningAlgValuesSupported, new Collection(), null) ??
+ _introspectionEndpointAuthSigningAlgValuesSupported;
///
/// Gets or sets the 'issuer'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.Issuer, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.Issuer)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public override string Issuer { get; set; }
///
/// Gets or sets the 'jwks_uri'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.JwksUri, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.JwksUri)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string JwksUri { get; set; }
///
/// Gets or sets the
///
+ [JsonIgnore]
public JsonWebKeySet JsonWebKeySet {get; set;}
///
/// Boolean value specifying whether the OP can pass a sid (session ID) query parameter to identify the RP session at the OP when the logout_uri is used. Dafault Value is false.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.LogoutSessionSupported, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.LogoutSessionSupported)]
public bool LogoutSessionSupported { get; set; }
///
/// Gets or sets the 'op_policy_uri'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.OpPolicyUri, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.OpPolicyUri)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string OpPolicyUri { get; set; }
///
/// Gets or sets the 'op_tos_uri'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.OpTosUri, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.OpTosUri)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string OpTosUri { get; set; }
///
/// Gets or sets the 'registration_endpoint'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RegistrationEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.RegistrationEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string RegistrationEndpoint { get; set; }
///
/// Gets the collection of 'request_object_encryption_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequestObjectEncryptionAlgValuesSupported, Required = Required.Default)]
- public ICollection RequestObjectEncryptionAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequestObjectEncryptionAlgValuesSupported)]
+ public ICollection RequestObjectEncryptionAlgValuesSupported =>
+ _requestObjectEncryptionAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _requestObjectEncryptionAlgValuesSupported, new Collection(), null) ??
+ _requestObjectEncryptionAlgValuesSupported;
///
/// Gets the collection of 'request_object_encryption_enc_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequestObjectEncryptionEncValuesSupported, Required = Required.Default)]
- public ICollection RequestObjectEncryptionEncValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequestObjectEncryptionEncValuesSupported)]
+ public ICollection RequestObjectEncryptionEncValuesSupported =>
+ _requestObjectEncryptionEncValuesSupported ??
+ Interlocked.CompareExchange(ref _requestObjectEncryptionEncValuesSupported, new Collection(), null) ??
+ _requestObjectEncryptionEncValuesSupported;
///
/// Gets the collection of 'request_object_signing_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequestObjectSigningAlgValuesSupported, Required = Required.Default)]
- public ICollection RequestObjectSigningAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequestObjectSigningAlgValuesSupported)]
+ public ICollection RequestObjectSigningAlgValuesSupported =>
+ _requestObjectSigningAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _requestObjectSigningAlgValuesSupported, new Collection(), null) ??
+ _requestObjectSigningAlgValuesSupported;
///
/// Gets or sets the 'request_parameter_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequestParameterSupported, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequestParameterSupported)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public bool RequestParameterSupported { get; set; }
///
/// Gets or sets the 'request_uri_parameter_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequestUriParameterSupported, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequestUriParameterSupported)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public bool RequestUriParameterSupported { get; set; }
///
/// Gets or sets the 'require_request_uri_registration'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.RequireRequestUriRegistration, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.RequireRequestUriRegistration)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public bool RequireRequestUriRegistration { get; set; }
///
/// Gets the collection of 'response_modes_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ResponseModesSupported, Required = Required.Default)]
- public ICollection ResponseModesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ResponseModesSupported)]
+ public ICollection ResponseModesSupported =>
+ _responseModesSupported ??
+ Interlocked.CompareExchange(ref _responseModesSupported, new Collection(), null) ??
+ _responseModesSupported;
///
/// Gets the collection of 'response_types_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ResponseTypesSupported, Required = Required.Default)]
- public ICollection ResponseTypesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ResponseTypesSupported)]
+ public ICollection ResponseTypesSupported =>
+ _responseTypesSupported ??
+ Interlocked.CompareExchange(ref _responseTypesSupported, new Collection(), null) ??
+ _responseTypesSupported;
///
/// Gets or sets the 'service_documentation'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ServiceDocumentation, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.ServiceDocumentation)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string ServiceDocumentation { get; set; }
///
/// Gets the collection of 'scopes_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.ScopesSupported, Required = Required.Default)]
- public ICollection ScopesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.ScopesSupported)]
+ public ICollection ScopesSupported =>
+ _scopesSupported ??
+ Interlocked.CompareExchange(ref _scopesSupported, new Collection(), null) ??
+ _scopesSupported;
///
/// Gets the that the IdentityProvider indicates are to be used signing tokens.
@@ -313,13 +450,19 @@ public OpenIdConnectConfiguration(string json)
///
/// Gets the collection of 'subject_types_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.SubjectTypesSupported, Required = Required.Default)]
- public ICollection SubjectTypesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.SubjectTypesSupported)]
+ public ICollection SubjectTypesSupported =>
+ _subjectTypesSupported ??
+ Interlocked.CompareExchange(ref _subjectTypesSupported, new Collection(), null) ??
+ _subjectTypesSupported;
///
/// Gets or sets the 'token_endpoint'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.TokenEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.TokenEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public override string TokenEndpoint { get; set; }
///
@@ -331,46 +474,69 @@ public OpenIdConnectConfiguration(string json)
///
/// Gets the collection of 'token_endpoint_auth_methods_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.TokenEndpointAuthMethodsSupported, Required = Required.Default)]
- public ICollection TokenEndpointAuthMethodsSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.TokenEndpointAuthMethodsSupported)]
+ public ICollection TokenEndpointAuthMethodsSupported =>
+ _tokenEndpointAuthMethodsSupported ??
+ Interlocked.CompareExchange(ref _tokenEndpointAuthMethodsSupported, new Collection(), null) ??
+ _tokenEndpointAuthMethodsSupported;
///
/// Gets the collection of 'token_endpoint_auth_signing_alg_values_supported'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.TokenEndpointAuthSigningAlgValuesSupported, Required = Required.Default)]
- public ICollection TokenEndpointAuthSigningAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.TokenEndpointAuthSigningAlgValuesSupported)]
+ public ICollection TokenEndpointAuthSigningAlgValuesSupported =>
+ _tokenEndpointAuthSigningAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _tokenEndpointAuthSigningAlgValuesSupported, new Collection(), null) ??
+ _tokenEndpointAuthSigningAlgValuesSupported;
///
/// Gets the collection of 'ui_locales_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.UILocalesSupported, Required = Required.Default)]
- public ICollection UILocalesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.UILocalesSupported)]
+ public ICollection UILocalesSupported =>
+ _uILocalesSupported ??
+ Interlocked.CompareExchange(ref _uILocalesSupported, new Collection(), null) ??
+ _uILocalesSupported;
///
/// Gets or sets the 'user_info_endpoint'.
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.UserInfoEndpoint, Required = Required.Default)]
+ [JsonPropertyName(OpenIdProviderMetadataNames.UserInfoEndpoint)]
+#if NET6_0_OR_GREATER
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+#endif
public string UserInfoEndpoint { get; set; }
///
/// Gets the collection of 'userinfo_encryption_alg_values_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.UserInfoEncryptionAlgValuesSupported, Required = Required.Default)]
- public ICollection UserInfoEndpointEncryptionAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.UserInfoEncryptionAlgValuesSupported)]
+ public ICollection UserInfoEndpointEncryptionAlgValuesSupported =>
+ _userInfoEndpointEncryptionAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _userInfoEndpointEncryptionAlgValuesSupported, new Collection(), null) ??
+ _userInfoEndpointEncryptionAlgValuesSupported;
///
/// Gets the collection of 'userinfo_encryption_enc_values_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.UserInfoEncryptionEncValuesSupported, Required = Required.Default)]
- public ICollection UserInfoEndpointEncryptionEncValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.UserInfoEncryptionEncValuesSupported)]
+ public ICollection UserInfoEndpointEncryptionEncValuesSupported =>
+ _userInfoEndpointEncryptionEncValuesSupported ??
+ Interlocked.CompareExchange(ref _userInfoEndpointEncryptionEncValuesSupported, new Collection(), null) ??
+ _userInfoEndpointEncryptionEncValuesSupported;
///
/// Gets the collection of 'userinfo_signing_alg_values_supported'
///
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, PropertyName = OpenIdProviderMetadataNames.UserInfoSigningAlgValuesSupported, Required = Required.Default)]
- public ICollection UserInfoEndpointSigningAlgValuesSupported { get; } = new Collection();
+ [JsonPropertyName(OpenIdProviderMetadataNames.UserInfoSigningAlgValuesSupported)]
+ public ICollection UserInfoEndpointSigningAlgValuesSupported =>
+ _userInfoEndpointSigningAlgValuesSupported ??
+ Interlocked.CompareExchange(ref _userInfoEndpointSigningAlgValuesSupported, new Collection(), null) ??
+ _userInfoEndpointSigningAlgValuesSupported;
-#region shouldserialize
+ #region shouldserialize
+ // TODO - should we keep these, they were used by Newtonsoft to control serialization of collections.
+ // May help users to keep them hanging around.
///
/// Gets a bool that determines if the 'acr_values_supported' (AcrValuesSupported) property should be serialized.
/// This is used by Json.NET in order to conditionally serialize properties.
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs
index 22f7dc5421..17b354b390 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs
@@ -7,7 +7,6 @@
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json;
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
{
@@ -68,7 +67,7 @@ public static async Task GetAsync(string address, ID
if (LogHelper.IsEnabled(EventLogLevel.Verbose))
LogHelper.LogVerbose(LogMessages.IDX21811, doc);
- OpenIdConnectConfiguration openIdConnectConfiguration = JsonConvert.DeserializeObject(doc);
+ OpenIdConnectConfiguration openIdConnectConfiguration = OpenIdConnectConfigurationSerializer.Read(doc);
if (!string.IsNullOrEmpty(openIdConnectConfiguration.JwksUri))
{
if (LogHelper.IsEnabled(EventLogLevel.Verbose))
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs
new file mode 100644
index 0000000000..1eace64244
--- /dev/null
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs
@@ -0,0 +1,610 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using Microsoft.IdentityModel.Logging;
+using Utf8Bytes = Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdProviderMetadataUtf8Bytes;
+using JsonPrimitives = Microsoft.IdentityModel.Tokens.Json.JsonSerializerPrimitives;
+using MetadataName = Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdProviderMetadataNames;
+
+namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
+{
+ internal static class OpenIdConnectConfigurationSerializer
+ {
+ public const string ClassName = OpenIdConnectConfiguration.ClassName;
+
+ // This is used to perform performant case-insensitive property names.
+ // 6x used Newtonsoft and was case-insensitive w.r.t. property names.
+ // The serializer is written to use Utf8JsonReader.ValueTextEquals(...), to match property names.
+ // When we do not have a match, we check the uppercase name of the property against this table.
+ // If not found, then we assume we should put the value into AdditionalData.
+ // If we didn't do that, we would pay a performance penalty for those cases where there is AdditionalData
+ // but otherwise the JSON properties are all lower case.
+ public static HashSet OpenIdProviderMetadataNamesUpperCase = new HashSet
+ {
+ "ACR_VALUES_SUPPORTED",
+ "AUTHORIZATION_ENDPOINT",
+ "CHECK_SESSION_IFRAME",
+ "CLAIMS_LOCALES_SUPPORTED",
+ "CLAIMS_PARAMETER_SUPPORTED",
+ "CLAIMS_SUPPORTED",
+ "CLAIM_TYPES_SUPPORTED",
+ ".WELL-KNOWN/OPENID-CONFIGURATION",
+ "DISPLAY_VALUES_SUPPORTED",
+ "END_SESSION_ENDPOINT",
+ "FRONTCHANNEL_LOGOUT_SESSION_SUPPORTED",
+ "FRONTCHANNEL_LOGOUT_SUPPORTED",
+ "HTTP_LOGOUT_SUPPORTED",
+ "GRANT_TYPES_SUPPORTED",
+ "ID_TOKEN_ENCRYPTION_ALG_VALUES_SUPPORTED",
+ "ID_TOKEN_ENCRYPTION_ENC_VALUES_SUPPORTED",
+ "ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED",
+ "INTROSPECTION_ENDPOINT",
+ "INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED",
+ "INTROSPECTION_ENDPOINT_AUTH_SIGNING_ALG_VALUES_SUPPORTED",
+ "JWKS_URI",
+ "ISSUER",
+ "LOGOUT_SESSION_SUPPORTED",
+ "OP_POLICY_URI",
+ "OP_TOS_URI",
+ "REGISTRATION_ENDPOINT",
+ "REQUEST_OBJECT_ENCRYPTION_ALG_VALUES_SUPPORTED",
+ "REQUEST_OBJECT_ENCRYPTION_ENC_VALUES_SUPPORTED",
+ "REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED",
+ "REQUEST_PARAMETER_SUPPORTED",
+ "REQUEST_URI_PARAMETER_SUPPORTED",
+ "REQUIRE_REQUEST_URI_REGISTRATION",
+ "RESPONSE_MODES_SUPPORTED",
+ "RESPONSE_TYPES_SUPPORTED",
+ "SERVICE_DOCUMENTATION",
+ "SCOPES_SUPPORTED",
+ "SUBJECT_TYPES_SUPPORTED",
+ "TOKEN_ENDPOINT",
+ "TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED",
+ "TOKEN_ENDPOINT_AUTH_SIGNING_ALG_VALUES_SUPPORTED",
+ "UI_LOCALES_SUPPORTED",
+ "USERINFO_ENDPOINT",
+ "USERINFO_ENCRYPTION_ALG_VALUES_SUPPORTED",
+ "USERINFO_ENCRYPTION_ENC_VALUES_SUPPORTED",
+ "USERINFO_SIGNING_ALG_VALUES_SUPPORTED",
+ };
+
+ #region Read
+ public static OpenIdConnectConfiguration Read(string json)
+ {
+ return Read(json, new OpenIdConnectConfiguration());
+ }
+
+ public static OpenIdConnectConfiguration Read(string json, OpenIdConnectConfiguration config)
+ {
+ Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json).AsSpan());
+ return Read(ref reader, config);
+ }
+
+ ///
+ /// Reads config. see: https://openid.net/specs/openid-connect-discovery-1_0.html
+ ///
+ /// a pointing at a StartObject.
+ ///
+ /// A .
+ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdConnectConfiguration config)
+ {
+ if (!JsonPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, false))
+ throw LogHelper.LogExceptionMessage(
+ new JsonException(
+ LogHelper.FormatInvariant(
+ Tokens.LogMessages.IDX11023,
+ LogHelper.MarkAsNonPII("JsonTokenType.StartObject"),
+ LogHelper.MarkAsNonPII(reader.TokenType),
+ LogHelper.MarkAsNonPII(ClassName),
+ LogHelper.MarkAsNonPII(reader.TokenStartIndex),
+ LogHelper.MarkAsNonPII(reader.CurrentDepth),
+ LogHelper.MarkAsNonPII(reader.BytesConsumed))));
+
+ while(JsonPrimitives.ReaderRead(ref reader))
+ {
+ #region Check property name using ValueTextEquals
+ // the config spec, https://datatracker.ietf.org/doc/html/rfc7517#section-4, does not require that we reject JSON with
+ // duplicate member names, in strict mode, we could add logic to try a property once and throw if a duplicate shows up.
+ // 6x uses the last value.
+ // TODO - With collections, make sure two properties are not additive
+ if (reader.TokenType == JsonTokenType.PropertyName)
+ {
+ if (reader.ValueTextEquals(Utf8Bytes.AcrValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.AcrValuesSupported, MetadataName.AcrValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationEndpoint))
+ config.AuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.AuthorizationEndpoint, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.CheckSessionIframe))
+ config.CheckSessionIframe = JsonPrimitives.ReadString(ref reader, MetadataName.CheckSessionIframe, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ClaimsLocalesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimsLocalesSupported, MetadataName.ClaimsLocalesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ClaimsParameterSupported))
+ config.ClaimsParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.ClaimsParameterSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ClaimsSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimsSupported, MetadataName.ClaimsSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ClaimTypesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimTypesSupported, MetadataName.ClaimTypesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.DisplayValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.DisplayValuesSupported, MetadataName.DisplayValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.EndSessionEndpoint))
+ config.EndSessionEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.EndSessionEndpoint, ClassName, true);
+
+ // TODO these two properties are per spec 'boolean', we shipped 6x with them as string, if we change we may break folks.
+ // probably best to mark the property obsolete with the gentle tag, then open up another property and keep them in sync,
+ // remove the obsolete in 8.x
+ else if (reader.ValueTextEquals(Utf8Bytes.FrontchannelLogoutSessionSupported))
+ {
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.True)
+ config.FrontchannelLogoutSessionSupported = "True";
+ else if (reader.TokenType == JsonTokenType.False)
+ config.FrontchannelLogoutSessionSupported = "False";
+ else
+ config.FrontchannelLogoutSessionSupported = JsonPrimitives.ReadString(ref reader, MetadataName.FrontchannelLogoutSessionSupported, ClassName, false);
+ }
+ else if (reader.ValueTextEquals(Utf8Bytes.FrontchannelLogoutSupported))
+ {
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.True)
+ config.FrontchannelLogoutSupported = "True";
+ else if (reader.TokenType == JsonTokenType.False)
+ config.FrontchannelLogoutSupported = "False";
+ else
+ config.FrontchannelLogoutSupported = JsonPrimitives.ReadString(ref reader, MetadataName.FrontchannelLogoutSupported, ClassName, false);
+ }
+ else if (reader.ValueTextEquals(Utf8Bytes.GrantTypesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.GrantTypesSupported, MetadataName.GrantTypesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.HttpLogoutSupported))
+ config.HttpLogoutSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.HttpLogoutSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IdTokenEncryptionAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenEncryptionAlgValuesSupported, MetadataName.IdTokenEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IdTokenEncryptionEncValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenEncryptionEncValuesSupported, MetadataName.IdTokenEncryptionEncValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IdTokenSigningAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenSigningAlgValuesSupported, MetadataName.IdTokenSigningAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IntrospectionEndpoint))
+ config.IntrospectionEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.IntrospectionEndpoint, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IntrospectionEndpointAuthMethodsSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthMethodsSupported, MetadataName.IntrospectionEndpointAuthMethodsSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.IntrospectionEndpointAuthSigningAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthSigningAlgValuesSupported, MetadataName.IntrospectionEndpointAuthSigningAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.Issuer))
+ config.Issuer = JsonPrimitives.ReadString(ref reader, MetadataName.Issuer, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.JwksUri))
+ config.JwksUri = JsonPrimitives.ReadString(ref reader, MetadataName.JwksUri, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.LogoutSessionSupported))
+ config.LogoutSessionSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.LogoutSessionSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.OpPolicyUri))
+ config.OpPolicyUri = JsonPrimitives.ReadString(ref reader, MetadataName.OpPolicyUri, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.OpTosUri))
+ config.OpTosUri = JsonPrimitives.ReadString(ref reader, MetadataName.OpTosUri, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RegistrationEndpoint))
+ config.RegistrationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.RegistrationEndpoint, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequestObjectEncryptionAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionAlgValuesSupported, MetadataName.RequestObjectEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequestObjectEncryptionEncValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionEncValuesSupported, MetadataName.RequestObjectEncryptionEncValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequestObjectSigningAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectSigningAlgValuesSupported, MetadataName.RequestObjectSigningAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequestParameterSupported))
+ config.RequestParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequestParameterSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequestUriParameterSupported))
+ config.RequestUriParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequestUriParameterSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.RequireRequestUriRegistration))
+ config.RequireRequestUriRegistration = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequireRequestUriRegistration, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ResponseModesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ResponseModesSupported, MetadataName.ResponseModesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ResponseTypesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ResponseTypesSupported, MetadataName.ResponseTypesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ScopesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.ScopesSupported, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.ServiceDocumentation))
+ config.ServiceDocumentation = JsonPrimitives.ReadString(ref reader, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.SubjectTypesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, MetadataName.SubjectTypesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.SubjectTypesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, MetadataName.SubjectTypesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.TokenEndpoint))
+ config.TokenEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.TokenEndpoint, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.TokenEndpointAuthMethodsSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthMethodsSupported, MetadataName.TokenEndpointAuthMethodsSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.TokenEndpointAuthSigningAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthSigningAlgValuesSupported, MetadataName.TokenEndpointAuthSigningAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.UILocalesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.UILocalesSupported, MetadataName.UILocalesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.UserInfoEncryptionAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionAlgValuesSupported, MetadataName.UserInfoEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.UserInfoEncryptionEncValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionEncValuesSupported, MetadataName.UserInfoEncryptionEncValuesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.UserInfoEndpoint))
+ config.UserInfoEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (reader.ValueTextEquals(Utf8Bytes.UserInfoSigningAlgValuesSupported))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointSigningAlgValuesSupported, MetadataName.UserInfoSigningAlgValuesSupported, ClassName, true);
+ #endregion
+ else
+ {
+ #region case-insensitive
+ string propertyName = JsonPrimitives.GetPropertyName(ref reader, OpenIdConnectConfiguration.ClassName, true);
+
+ // fallback to checking property names as case insensitive
+ // first check to see if the upper case property value is a valid property name if not add to AdditionalData, to avoid unnecessary string compares.
+ if (!OpenIdProviderMetadataNamesUpperCase.Contains(propertyName.ToUpperInvariant()))
+ {
+ config.AdditionalData[propertyName] = JsonPrimitives.GetUnknownProperty(ref reader);
+ }
+ else
+ {
+ if (propertyName.Equals(MetadataName.AcrValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.AcrValuesSupported, MetadataName.AcrValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.AuthorizationEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.AuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.AuthorizationEndpoint, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.CheckSessionIframe, StringComparison.OrdinalIgnoreCase))
+ config.CheckSessionIframe = JsonPrimitives.ReadString(ref reader, MetadataName.CheckSessionIframe, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.ClaimsLocalesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimsLocalesSupported, MetadataName.ClaimsLocalesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.ClaimsParameterSupported, StringComparison.OrdinalIgnoreCase))
+ config.ClaimsParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.ClaimsParameterSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.ClaimsSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimsSupported, MetadataName.ClaimsSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.ClaimTypesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ClaimTypesSupported, MetadataName.ClaimTypesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.DisplayValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.DisplayValuesSupported, MetadataName.DisplayValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.EndSessionEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.EndSessionEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.EndSessionEndpoint, ClassName, true);
+
+ // TODO these two properties are per spec 'boolean', we shipped 6x with them as string, if we change we may break folks.
+ // probably best to mark the property obsolete with the gentle tag, then open up another property and keep them in sync,
+ // remove the obsolete in 8.x
+ else if (propertyName.Equals(MetadataName.FrontchannelLogoutSessionSupported, StringComparison.OrdinalIgnoreCase))
+ {
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.True)
+ config.FrontchannelLogoutSessionSupported = "True";
+ else if (reader.TokenType == JsonTokenType.False)
+ config.FrontchannelLogoutSessionSupported = "False";
+ else
+ config.FrontchannelLogoutSessionSupported = JsonPrimitives.ReadString(ref reader, MetadataName.FrontchannelLogoutSessionSupported, ClassName, false);
+ }
+ else if (propertyName.Equals(MetadataName.FrontchannelLogoutSupported, StringComparison.OrdinalIgnoreCase))
+ {
+ reader.Read();
+ if (reader.TokenType == JsonTokenType.True)
+ config.FrontchannelLogoutSupported = "True";
+ else if (reader.TokenType == JsonTokenType.False)
+ config.FrontchannelLogoutSupported = "False";
+ else
+ config.FrontchannelLogoutSupported = JsonPrimitives.ReadString(ref reader, MetadataName.FrontchannelLogoutSupported, ClassName, false);
+ }
+ else if (propertyName.Equals(MetadataName.GrantTypesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.GrantTypesSupported, MetadataName.GrantTypesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.HttpLogoutSupported, StringComparison.OrdinalIgnoreCase))
+ config.HttpLogoutSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.HttpLogoutSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.IdTokenEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenEncryptionAlgValuesSupported, MetadataName.IdTokenEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.IdTokenEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenEncryptionEncValuesSupported, MetadataName.IdTokenEncryptionEncValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName.IdTokenSigningAlgValuesSupported , StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.IdTokenSigningAlgValuesSupported, MetadataName.IdTokenSigningAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. IntrospectionEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.IntrospectionEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.IntrospectionEndpoint, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. IntrospectionEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthMethodsSupported, MetadataName.IntrospectionEndpointAuthMethodsSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. IntrospectionEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthSigningAlgValuesSupported, MetadataName.IntrospectionEndpointAuthSigningAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. Issuer, StringComparison.OrdinalIgnoreCase))
+ config.Issuer = JsonPrimitives.ReadString(ref reader, MetadataName.Issuer, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. JwksUri, StringComparison.OrdinalIgnoreCase))
+ config.JwksUri = JsonPrimitives.ReadString(ref reader, MetadataName.JwksUri, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. LogoutSessionSupported, StringComparison.OrdinalIgnoreCase))
+ config.LogoutSessionSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.LogoutSessionSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. OpPolicyUri, StringComparison.OrdinalIgnoreCase))
+ config.OpPolicyUri = JsonPrimitives.ReadString(ref reader, MetadataName.OpPolicyUri, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. OpTosUri, StringComparison.OrdinalIgnoreCase))
+ config.OpTosUri = JsonPrimitives.ReadString(ref reader, MetadataName.OpTosUri, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RegistrationEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.RegistrationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.RegistrationEndpoint, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequestObjectEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionAlgValuesSupported, MetadataName.RequestObjectEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequestObjectEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionEncValuesSupported, MetadataName.RequestObjectEncryptionEncValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequestObjectSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.RequestObjectSigningAlgValuesSupported, MetadataName.RequestObjectSigningAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequestParameterSupported, StringComparison.OrdinalIgnoreCase))
+ config.RequestParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequestParameterSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequestUriParameterSupported, StringComparison.OrdinalIgnoreCase))
+ config.RequestUriParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequestUriParameterSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. RequireRequestUriRegistration, StringComparison.OrdinalIgnoreCase))
+ config.RequireRequestUriRegistration = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequireRequestUriRegistration, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. ResponseModesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ResponseModesSupported, MetadataName.ResponseModesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. ResponseTypesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ResponseTypesSupported, MetadataName.ResponseTypesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. ScopesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.ScopesSupported, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. ServiceDocumentation, StringComparison.OrdinalIgnoreCase))
+ config.ServiceDocumentation = JsonPrimitives.ReadString(ref reader, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. SubjectTypesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, MetadataName.SubjectTypesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. TokenEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.TokenEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.TokenEndpoint, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. TokenEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthMethodsSupported, MetadataName.TokenEndpointAuthMethodsSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. TokenEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthSigningAlgValuesSupported, MetadataName.TokenEndpointAuthSigningAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. UILocalesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.UILocalesSupported, MetadataName.UILocalesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. UserInfoEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionAlgValuesSupported, MetadataName.UserInfoEncryptionAlgValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. UserInfoEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionEncValuesSupported, MetadataName.UserInfoEncryptionEncValuesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. UserInfoEndpoint, StringComparison.OrdinalIgnoreCase))
+ config.UserInfoEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.ScopesSupported, ClassName, true);
+
+ else if (propertyName.Equals(MetadataName. UserInfoSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase))
+ JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointSigningAlgValuesSupported, MetadataName.UserInfoSigningAlgValuesSupported, ClassName, true);
+
+ }
+ #endregion case-insensitive
+ }
+ }
+
+ if (JsonPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.EndObject, false))
+ break;
+ }
+
+ return config;
+ }
+ #endregion
+
+ #region Write
+ public static string Write(OpenIdConnectConfiguration OpenIdConnectConfiguration)
+ {
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ Utf8JsonWriter writer = null;
+ try
+ {
+ writer = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
+ Write(ref writer, OpenIdConnectConfiguration);
+ writer.Flush();
+ return Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
+ }
+ finally
+ {
+ writer?.Dispose();
+ }
+ }
+ }
+
+ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration config)
+ {
+ writer.WriteStartObject();
+
+ if (config.AcrValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.AcrValuesSupported, config.AcrValuesSupported);
+
+ if (!string.IsNullOrEmpty(config.AuthorizationEndpoint))
+ writer.WriteString(Utf8Bytes.AuthorizationEndpoint, config.AuthorizationEndpoint);
+
+ if (!string.IsNullOrEmpty(config.CheckSessionIframe))
+ writer.WriteString(Utf8Bytes.CheckSessionIframe, config.CheckSessionIframe);
+
+ if (config.ClaimsSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ClaimsSupported, config.ClaimsSupported);
+
+ if (config.ClaimsLocalesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ClaimsLocalesSupported, config.ClaimsLocalesSupported);
+
+ if (config.ClaimsParameterSupported)
+ writer.WriteBoolean(Utf8Bytes.ClaimsParameterSupported, config.ClaimsParameterSupported);
+
+ if (config.ClaimTypesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ClaimTypesSupported, config.ClaimTypesSupported);
+
+ if (config.DisplayValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.DisplayValuesSupported, config.DisplayValuesSupported);
+
+ if (!string.IsNullOrEmpty(config.EndSessionEndpoint))
+ writer.WriteString(Utf8Bytes.EndSessionEndpoint, config.EndSessionEndpoint);
+
+ if (!string.IsNullOrEmpty(config.FrontchannelLogoutSessionSupported))
+ writer.WriteString(Utf8Bytes.FrontchannelLogoutSessionSupported, config.FrontchannelLogoutSessionSupported);
+
+ if (!string.IsNullOrEmpty(config.FrontchannelLogoutSupported))
+ writer.WriteString(Utf8Bytes.FrontchannelLogoutSupported, config.FrontchannelLogoutSupported);
+
+ if (config.GrantTypesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.GrantTypesSupported, config.GrantTypesSupported);
+
+ if (config.HttpLogoutSupported)
+ writer.WriteBoolean(Utf8Bytes.HttpLogoutSupported, config.HttpLogoutSupported);
+
+ if (config.IdTokenEncryptionAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.IdTokenEncryptionAlgValuesSupported, config.IdTokenEncryptionAlgValuesSupported);
+
+ if (config.IdTokenEncryptionEncValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.IdTokenEncryptionEncValuesSupported, config.IdTokenEncryptionEncValuesSupported);
+
+ if (config.IdTokenSigningAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.IdTokenSigningAlgValuesSupported, config.IdTokenSigningAlgValuesSupported);
+
+ if (!string.IsNullOrEmpty(config.IntrospectionEndpoint))
+ writer.WriteString(Utf8Bytes.IntrospectionEndpoint, config.IntrospectionEndpoint);
+
+ if (config.IntrospectionEndpointAuthMethodsSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.IntrospectionEndpointAuthMethodsSupported, config.IntrospectionEndpointAuthMethodsSupported);
+
+ if (config.IntrospectionEndpointAuthSigningAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.IntrospectionEndpointAuthSigningAlgValuesSupported, config.IntrospectionEndpointAuthSigningAlgValuesSupported);
+
+ if (!string.IsNullOrEmpty(config.Issuer))
+ writer.WriteString(Utf8Bytes.Issuer, config.Issuer);
+
+ if (!string.IsNullOrEmpty(config.JwksUri))
+ writer.WriteString(Utf8Bytes.JwksUri, config.JwksUri);
+
+ if (config.LogoutSessionSupported)
+ writer.WriteBoolean(Utf8Bytes.LogoutSessionSupported, config.LogoutSessionSupported);
+
+ if (!string.IsNullOrEmpty(config.OpPolicyUri))
+ writer.WriteString(Utf8Bytes.OpPolicyUri, config.OpPolicyUri);
+
+ if (!string.IsNullOrEmpty(config.OpTosUri))
+ writer.WriteString(Utf8Bytes.OpTosUri, config.OpTosUri);
+
+ if (!string.IsNullOrEmpty(config.RegistrationEndpoint))
+ writer.WriteString(Utf8Bytes.RegistrationEndpoint, config.RegistrationEndpoint);
+
+ if (config.RequestObjectEncryptionAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RequestObjectEncryptionAlgValuesSupported, config.RequestObjectEncryptionAlgValuesSupported);
+
+ if (config.RequestObjectEncryptionEncValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RequestObjectEncryptionEncValuesSupported, config.RequestObjectEncryptionEncValuesSupported);
+
+ if (config.RequestObjectSigningAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RequestObjectSigningAlgValuesSupported, config.RequestObjectSigningAlgValuesSupported);
+
+ if (config.RequestParameterSupported)
+ writer.WriteBoolean(Utf8Bytes.RequestParameterSupported, config.RequestParameterSupported);
+
+ if (config.RequestUriParameterSupported)
+ writer.WriteBoolean(Utf8Bytes.RequestUriParameterSupported, config.RequestUriParameterSupported);
+
+ if (config.RequireRequestUriRegistration)
+ writer.WriteBoolean(Utf8Bytes.RequireRequestUriRegistration, config.RequireRequestUriRegistration);
+
+ if (config.ResponseModesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ResponseModesSupported, config.ResponseModesSupported);
+
+ if (config.ResponseTypesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ResponseTypesSupported, config.ResponseTypesSupported);
+
+ if (config.ScopesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ScopesSupported, config.ScopesSupported);
+
+ if (!string.IsNullOrEmpty(config.ServiceDocumentation))
+ writer.WriteString(Utf8Bytes.ServiceDocumentation, config.ServiceDocumentation);
+
+ if (config.SubjectTypesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.SubjectTypesSupported, config.SubjectTypesSupported);
+
+ if (!string.IsNullOrEmpty(config.TokenEndpoint))
+ writer.WriteString(Utf8Bytes.TokenEndpoint, config.TokenEndpoint);
+
+ if (config.TokenEndpointAuthMethodsSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.TokenEndpointAuthMethodsSupported, config.TokenEndpointAuthMethodsSupported);
+
+ if (config.TokenEndpointAuthSigningAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.TokenEndpointAuthSigningAlgValuesSupported, config.TokenEndpointAuthSigningAlgValuesSupported);
+
+ if (config.UILocalesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.UILocalesSupported, config.UILocalesSupported);
+
+ if (!string.IsNullOrEmpty(config.UserInfoEndpoint))
+ writer.WriteString(Utf8Bytes.UserInfoEndpoint, config.UserInfoEndpoint);
+
+ if (config.UserInfoEndpointEncryptionAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.UserInfoEncryptionAlgValuesSupported, config.UserInfoEndpointEncryptionAlgValuesSupported);
+
+ if (config.UserInfoEndpointEncryptionEncValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.UserInfoEncryptionEncValuesSupported, config.UserInfoEndpointEncryptionEncValuesSupported);
+
+ if (config.UserInfoEndpointSigningAlgValuesSupported.Count > 0)
+ JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.UserInfoSigningAlgValuesSupported, config.UserInfoEndpointSigningAlgValuesSupported);
+
+ if (config.AdditionalData.Count > 0)
+ JsonPrimitives.WriteAdditionalData(ref writer, config.AdditionalData);
+
+ writer.WriteEndObject();
+ }
+ #endregion
+ }
+}
+
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectMessage.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectMessage.cs
index 6f4e45c955..7ed8fa69c5 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectMessage.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectMessage.cs
@@ -5,8 +5,10 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
+using System.Text;
+using System.Text.Json;
using Microsoft.IdentityModel.Logging;
-using Newtonsoft.Json.Linq;
+using JsonPrimitives = Microsoft.IdentityModel.Tokens.Json.JsonSerializerPrimitives;
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
{
@@ -15,6 +17,8 @@ namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
///
public class OpenIdConnectMessage : AuthenticationProtocolMessage
{
+ internal const string ClassName = "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage";
+
///
/// Initializes a new instance of the class.
///
@@ -30,7 +34,7 @@ public OpenIdConnectMessage(string json)
try
{
- SetJsonParameters(JObject.Parse(json));
+ SetJsonParameters(json);
}
catch
{
@@ -82,7 +86,7 @@ public OpenIdConnectMessage(NameValueCollection nameValueCollection)
///
/// Initializes a new instance of the class.
///
- /// Enumeration of key value pairs.
+ /// Enumeration of key value pairs.
public OpenIdConnectMessage(IEnumerable> parameters)
{
if (parameters == null)
@@ -104,30 +108,20 @@ public OpenIdConnectMessage(IEnumerable> paramete
}
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The JSON object from which the instance is created.
- [Obsolete("The 'OpenIdConnectMessage(object json)' constructor is obsolete. Please use 'OpenIdConnectMessage(string json)' instead.")]
- public OpenIdConnectMessage(object json)
- {
- if (json == null)
- throw LogHelper.LogArgumentNullException(nameof(json));
-
- var jObject = JObject.Parse(json.ToString());
- SetJsonParameters(jObject);
- }
-
- private void SetJsonParameters(JObject json)
+ private void SetJsonParameters(string json)
{
- if (json == null)
- throw LogHelper.LogArgumentNullException("json");
+ Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json).AsSpan());
- foreach (var pair in json)
+ while (JsonPrimitives.ReaderRead(ref reader))
{
- if (json.TryGetValue(pair.Key, out JToken value))
+ if (reader.TokenType == JsonTokenType.PropertyName)
{
- SetParameter(pair.Key, value.ToString());
+ string propertyName = JsonPrimitives.GetPropertyName(ref reader, ClassName, true);
+ string propertyValue = null;
+ if (reader.TokenType == JsonTokenType.String)
+ propertyValue = JsonPrimitives.ReadString(ref reader, propertyName, ClassName);
+
+ SetParameter(propertyName, propertyValue);
}
}
}
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectParameterNames.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectParameterNames.cs
index 7a5903d484..623fd73ff1 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectParameterNames.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectParameterNames.cs
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
+
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
{
///
@@ -53,4 +55,53 @@ public static class OpenIdConnectParameterNames
public const string VersionTelemetry = "x-client-ver";
#pragma warning restore 1591
}
+
+ ///
+ /// Parameter names for OpenIdConnect UTF8 bytes.
+ ///
+ internal static class OpenIdConnectParameterUtf8Bytes
+ {
+ public static ReadOnlySpan AccessToken => "access_token"u8;
+ public static ReadOnlySpan AcrValues => "acr_values"u8;
+ public static ReadOnlySpan ClaimsLocales => "claims_locales"u8;
+ public static ReadOnlySpan ClientAssertion => "client_assertion"u8;
+ public static ReadOnlySpan ClientAssertionType => "client_assertion_type"u8;
+ public static ReadOnlySpan ClientId => "client_id"u8;
+ public static ReadOnlySpan ClientSecret => "client_secret"u8;
+ public static ReadOnlySpan Code => "code"u8;
+ public static ReadOnlySpan Display => "display"u8;
+ public static ReadOnlySpan DomainHint => "domain_hint"u8;
+ public static ReadOnlySpan Error => "error"u8;
+ public static ReadOnlySpan ErrorDescription => "error_description"u8;
+ public static ReadOnlySpan ErrorUri => "error_uri"u8;
+ public static ReadOnlySpan ExpiresIn => "expires_in"u8;
+ public static ReadOnlySpan GrantType => "grant_type"u8;
+ public static ReadOnlySpan Iss => "iss"u8;
+ public static ReadOnlySpan IdToken => "id_token"u8;
+ public static ReadOnlySpan IdTokenHint => "id_token_hint"u8;
+ public static ReadOnlySpan IdentityProvider => "identity_provider"u8;
+ public static ReadOnlySpan LoginHint => "login_hint"u8;
+ public static ReadOnlySpan MaxAge => "max_age"u8;
+ public static ReadOnlySpan Nonce => "nonce"u8;
+ public static ReadOnlySpan Password => "password"u8;
+ public static ReadOnlySpan PostLogoutRedirectUri => "post_logout_redirect_uri"u8;
+ public static ReadOnlySpan Prompt => "prompt"u8;
+ public static ReadOnlySpan RedirectUri => "redirect_uri"u8;
+ public static ReadOnlySpan RefreshToken => "refresh_token"u8;
+ public static ReadOnlySpan RequestUri => "request_uri"u8;
+ public static ReadOnlySpan Resource => "resource"u8;
+ public static ReadOnlySpan ResponseMode => "response_mode"u8;
+ public static ReadOnlySpan ResponseType => "response_type"u8;
+ public static ReadOnlySpan Scope => "scope"u8;
+ public static ReadOnlySpan SkuTelemetry => "x-client-SKU"u8;
+ public static ReadOnlySpan SessionState => "session_state"u8;
+ public static ReadOnlySpan Sid => "sid"u8;
+ public static ReadOnlySpan State => "state"u8;
+ public static ReadOnlySpan TargetLinkUri => "target_link_uri"u8;
+ public static ReadOnlySpan TokenType => "token_type"u8;
+ public static ReadOnlySpan UiLocales => "ui_locales"u8;
+ public static ReadOnlySpan UserId => "user_id"u8;
+ public static ReadOnlySpan Username => "username"u8;
+ public static ReadOnlySpan VersionTelemetry => "x-client-ver"u8;
+ }
}
diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs
index 36057d8708..4f2cb60bbb 100644
--- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs
+++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs
@@ -1,10 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
+
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
{
///
- /// OpenIdProviderConfiguration Names
+ /// OpenIdProviderConfiguration MetadataName
/// http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
///
public static class OpenIdProviderMetadataNames
@@ -58,4 +60,58 @@ public static class OpenIdProviderMetadataNames
public const string UserInfoSigningAlgValuesSupported = "userinfo_signing_alg_values_supported";
#pragma warning restore 1591
}
+
+ ///
+ /// OpenIdProviderConfiguration MetadataName - UTF8Bytes
+ /// http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
+ ///
+ internal static class OpenIdProviderMetadataUtf8Bytes
+ {
+ public static ReadOnlySpan AcrValuesSupported => "acr_values_supported"u8;
+ public static ReadOnlySpan AuthorizationEndpoint => "authorization_endpoint"u8;
+ public static ReadOnlySpan CheckSessionIframe => "check_session_iframe"u8;
+ public static ReadOnlySpan ClaimsLocalesSupported => "claims_locales_supported"u8;
+ public static ReadOnlySpan ClaimsParameterSupported => "claims_parameter_supported"u8;
+ public static ReadOnlySpan ClaimsSupported => "claims_supported"u8;
+ public static ReadOnlySpan ClaimTypesSupported => "claim_types_supported"u8;
+ public static ReadOnlySpan Discovery => ".well-known/openid-configuration"u8;
+ public static ReadOnlySpan DisplayValuesSupported => "display_values_supported"u8;
+ public static ReadOnlySpan EndSessionEndpoint => "end_session_endpoint"u8;
+ public static ReadOnlySpan FrontchannelLogoutSessionSupported => "frontchannel_logout_session_supported"u8;
+ public static ReadOnlySpan FrontchannelLogoutSupported => "frontchannel_logout_supported"u8;
+ public static ReadOnlySpan HttpLogoutSupported => "http_logout_supported"u8;
+ public static ReadOnlySpan GrantTypesSupported => "grant_types_supported"u8;
+ public static ReadOnlySpan IdTokenEncryptionAlgValuesSupported => "id_token_encryption_alg_values_supported"u8;
+ public static ReadOnlySpan IdTokenEncryptionEncValuesSupported => "id_token_encryption_enc_values_supported"u8;
+ public static ReadOnlySpan IdTokenSigningAlgValuesSupported => "id_token_signing_alg_values_supported"u8;
+ public static ReadOnlySpan IntrospectionEndpoint => "introspection_endpoint"u8;
+ public static ReadOnlySpan IntrospectionEndpointAuthMethodsSupported => "introspection_endpoint_auth_methods_supported"u8;
+ public static ReadOnlySpan IntrospectionEndpointAuthSigningAlgValuesSupported => "introspection_endpoint_auth_signing_alg_values_supported"u8;
+ public static ReadOnlySpan JwksUri => "jwks_uri"u8;
+ public static ReadOnlySpan Issuer => "issuer"u8;
+ public static ReadOnlySpan LogoutSessionSupported => "logout_session_supported"u8;
+ public static ReadOnlySpan MicrosoftMultiRefreshToken => "microsoft_multi_refresh_token"u8;
+ public static ReadOnlySpan OpPolicyUri => "op_policy_uri"u8;
+ public static ReadOnlySpan OpTosUri => "op_tos_uri"u8;
+ public static ReadOnlySpan RegistrationEndpoint => "registration_endpoint"u8;
+ public static ReadOnlySpan RequestObjectEncryptionAlgValuesSupported => "request_object_encryption_alg_values_supported"u8;
+ public static ReadOnlySpan RequestObjectEncryptionEncValuesSupported => "request_object_encryption_enc_values_supported"u8;
+ public static ReadOnlySpan RequestObjectSigningAlgValuesSupported => "request_object_signing_alg_values_supported"u8;
+ public static ReadOnlySpan RequestParameterSupported => "request_parameter_supported"u8;
+ public static ReadOnlySpan RequestUriParameterSupported => "request_uri_parameter_supported"u8;
+ public static ReadOnlySpan RequireRequestUriRegistration => "require_request_uri_registration"u8;
+ public static ReadOnlySpan ResponseModesSupported => "response_modes_supported"u8;
+ public static ReadOnlySpan ResponseTypesSupported => "response_types_supported"u8;
+ public static ReadOnlySpan ServiceDocumentation => "service_documentation"u8;
+ public static ReadOnlySpan ScopesSupported => "scopes_supported"u8;
+ public static ReadOnlySpan SubjectTypesSupported => "subject_types_supported"u8;
+ public static ReadOnlySpan TokenEndpoint => "token_endpoint"u8;
+ public static ReadOnlySpan TokenEndpointAuthMethodsSupported => "token_endpoint_auth_methods_supported"u8;
+ public static ReadOnlySpan TokenEndpointAuthSigningAlgValuesSupported => "token_endpoint_auth_signing_alg_values_supported"u8;
+ public static ReadOnlySpan UILocalesSupported => "ui_locales_supported"u8;
+ public static ReadOnlySpan UserInfoEndpoint => "userinfo_endpoint"u8;
+ public static ReadOnlySpan UserInfoEncryptionAlgValuesSupported => "userinfo_encryption_alg_values_supported"u8;
+ public static ReadOnlySpan UserInfoEncryptionEncValuesSupported => "userinfo_encryption_enc_values_supported"u8;
+ public static ReadOnlySpan UserInfoSigningAlgValuesSupported => "userinfo_signing_alg_values_supported"u8;
+ }
}
diff --git a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestDescriptor.cs b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestDescriptor.cs
index 7f0b2893c9..bec080e981 100644
--- a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestDescriptor.cs
+++ b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestDescriptor.cs
@@ -1,28 +1,5 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
using System.Collections.Generic;
using Microsoft.IdentityModel.Logging;
diff --git a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs
index 73e215909c..d393ebbdf1 100644
--- a/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs
+++ b/src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestHandler.cs
@@ -1,43 +1,23 @@
-//------------------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation.
-// All rights reserved.
-//
-// This code is licensed under the MIT License.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using JsonPrimitives = Microsoft.IdentityModel.Tokens.Json.JsonSerializerPrimitives;
namespace Microsoft.IdentityModel.Protocols.SignedHttpRequest
{
@@ -79,30 +59,78 @@ public string CreateSignedHttpRequest(SignedHttpRequestDescriptor signedHttpRequ
/// A signed http request as a JWS in Compact Serialization Format.
public string CreateSignedHttpRequest(SignedHttpRequestDescriptor signedHttpRequestDescriptor, CallContext callContext)
{
+ if (callContext != null)
+ LogHelper.LogVerbose(callContext.ActivityId.ToString());
+
if (signedHttpRequestDescriptor == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor));
if (signedHttpRequestDescriptor.SigningCredentials == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor.SigningCredentials));
- var payload = CreateHttpRequestPayload(signedHttpRequestDescriptor, callContext);
- var header = new JObject();
- if (signedHttpRequestDescriptor.AdditionalHeaderClaims != null && signedHttpRequestDescriptor.AdditionalHeaderClaims.Count != 0)
+ string encodedPayload;
+ using (MemoryStream memoryStream = new MemoryStream())
{
- if (signedHttpRequestDescriptor.AdditionalHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any())
- throw LogHelper.LogExceptionMessage(new SecurityTokenException(LogHelper.FormatInvariant(JsonWebTokens.LogMessages.IDX14116, LogHelper.MarkAsNonPII(nameof(signedHttpRequestDescriptor.AdditionalHeaderClaims)), LogHelper.MarkAsNonPII(string.Join(", ", JwtTokenUtilities.DefaultHeaderParameters)))));
- header.Merge(JObject.FromObject(signedHttpRequestDescriptor.AdditionalHeaderClaims));
+ Utf8JsonWriter payloadWriter = null;
+ try
+ {
+ payloadWriter = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
+ payloadWriter.WriteStartObject();
+
+ CreateHttpRequestPayload(ref payloadWriter, signedHttpRequestDescriptor);
+
+ payloadWriter.WriteEndObject();
+ payloadWriter.Flush();
+ encodedPayload = Base64UrlEncoder.Encode(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
+ }
+ finally
+ {
+ payloadWriter?.Dispose();
+ }
}
- // set the "typ" header claim to "pop"
- // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-6.2
- header[JwtHeaderParameterNames.Alg] = signedHttpRequestDescriptor.SigningCredentials.Algorithm;
- header[JwtHeaderParameterNames.Typ] = SignedHttpRequestConstants.TokenType;
+ string encodedHeader;
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ Utf8JsonWriter headerWriter = null;
+ try
+ {
+ headerWriter = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
+ headerWriter.WriteStartObject();
+
+ if (signedHttpRequestDescriptor.AdditionalHeaderClaims != null && signedHttpRequestDescriptor.AdditionalHeaderClaims.Count != 0)
+ {
+ if (signedHttpRequestDescriptor.AdditionalHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any())
+ throw LogHelper.LogExceptionMessage(
+ new SecurityTokenException(
+ LogHelper.FormatInvariant(
+ JsonWebTokens.LogMessages.IDX14116,
+ LogHelper.MarkAsNonPII(nameof(signedHttpRequestDescriptor.AdditionalHeaderClaims)),
+ LogHelper.MarkAsNonPII(string.Join(", ", JwtTokenUtilities.DefaultHeaderParameters)))));
+ }
- var message = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(header.ToString(Formatting.None))) + "." + Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payload));
+ // set the "typ" header claim to "pop"
+ // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-6.2
+ headerWriter.WriteString(JwtHeaderParameterNames.Alg, signedHttpRequestDescriptor.SigningCredentials.Algorithm);
+ headerWriter.WriteString(JwtHeaderParameterNames.Typ, SignedHttpRequestConstants.TokenType);
+ if (signedHttpRequestDescriptor.AdditionalHeaderClaims != null)
+ foreach (string key in signedHttpRequestDescriptor.AdditionalHeaderClaims.Keys)
+ headerWriter.WriteString(key, signedHttpRequestDescriptor.AdditionalHeaderClaims[key].ToString());
- return message + "." + JwtTokenUtilities.CreateEncodedSignature(message, signedHttpRequestDescriptor.SigningCredentials, false);
+ headerWriter.WriteEndObject();
+ headerWriter.Flush();
+ encodedHeader = Base64UrlEncoder.Encode(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
+
+ }
+ finally
+ {
+ headerWriter?.Dispose();
+ }
+
+ string message = encodedHeader + "." + encodedPayload;
+ return message + "." + JwtTokenUtilities.CreateEncodedSignature(message, signedHttpRequestDescriptor.SigningCredentials, false);
+ }
}
///
@@ -114,187 +142,179 @@ public string CreateSignedHttpRequest(SignedHttpRequestDescriptor signedHttpRequ
///
/// Users can utilize to create additional claim(s) and add them to the signed http request.
///
- protected virtual string CreateHttpRequestPayload(SignedHttpRequestDescriptor signedHttpRequestDescriptor, CallContext callContext)
+ protected internal virtual string CreateHttpRequestPayload(SignedHttpRequestDescriptor signedHttpRequestDescriptor, CallContext callContext)
{
if (signedHttpRequestDescriptor == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor));
- Dictionary payload = new Dictionary();
+ Utf8JsonWriter writer = null;
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ try
+ {
+ writer = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
+ writer.WriteStartObject();
+
+ CreateHttpRequestPayload(ref writer, signedHttpRequestDescriptor);
+
+ writer.WriteEndObject();
+ writer.Flush();
+ return Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
+ }
+ finally
+ {
+ writer?.Dispose();
+ }
+ }
+ }
- AddAtClaim(payload, signedHttpRequestDescriptor);
+ internal void CreateHttpRequestPayload(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ {
+ AddAtClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateTs)
- AddTsClaim(payload, signedHttpRequestDescriptor);
+ AddTsClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateM)
- AddMClaim(payload, signedHttpRequestDescriptor);
+ AddMClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateU)
- AddUClaim(payload, signedHttpRequestDescriptor);
+ AddUClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateP)
- AddPClaim(payload, signedHttpRequestDescriptor);
+ AddPClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateQ)
- AddQClaim(payload, signedHttpRequestDescriptor);
+ AddQClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateH)
- AddHClaim(payload, signedHttpRequestDescriptor);
+ AddHClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateB)
- AddBClaim(payload, signedHttpRequestDescriptor);
+ AddBClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateNonce)
- AddNonceClaim(payload, signedHttpRequestDescriptor);
+ AddNonceClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.CreateCnf)
- AddCnfClaim(payload, signedHttpRequestDescriptor);
+ AddCnfClaim(ref writer, signedHttpRequestDescriptor);
if (signedHttpRequestDescriptor.AdditionalPayloadClaims != null && signedHttpRequestDescriptor.AdditionalPayloadClaims.Any())
- {
- foreach (var additionalPayloadClaim in signedHttpRequestDescriptor.AdditionalPayloadClaims)
- {
- if (!payload.ContainsKey(additionalPayloadClaim.Key))
- payload.Add(additionalPayloadClaim.Key, additionalPayloadClaim.Value);
- }
- }
-
- return JObject.FromObject(payload).ToString(Formatting.None);
+ JsonPrimitives.WriteAdditionalData(ref writer, signedHttpRequestDescriptor.AdditionalPayloadClaims);
}
///
- /// Adds the 'at' claim to the .
+ /// Adds the 'at' claim to the .
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
- internal virtual void AddAtClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ internal virtual void AddAtClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- payload.Add(SignedHttpRequestClaimTypes.At, signedHttpRequestDescriptor.AccessToken);
+ // TODO - use JsonEncodedText for property names
+ writer.WriteString(SignedHttpRequestClaimTypes.At, signedHttpRequestDescriptor.AccessToken);
}
///
- /// Adds the 'ts' claim to the .
+ /// Adds the 'ts' claim to the .
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
///
/// This method will be executed only if is set to true.
///
- internal virtual void AddTsClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ internal virtual void AddTsClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- var signedHttpRequestCreationTime = DateTime.UtcNow.Add(signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.TimeAdjustment);
- payload.Add(SignedHttpRequestClaimTypes.Ts, EpochTime.GetIntDate(signedHttpRequestCreationTime));
+ writer.WriteNumber(
+ SignedHttpRequestClaimTypes.Ts,
+ EpochTime.GetIntDate(
+ DateTime.UtcNow.Add(signedHttpRequestDescriptor.SignedHttpRequestCreationParameters.TimeAdjustment)));
}
///
- /// Adds the 'm' claim to the .
+ /// Adds the 'm' claim using the Utf8JsonWriter
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
///
/// This method will be executed only if is set to true.
///
- internal virtual void AddMClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ internal virtual void AddMClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- var httpMethod = signedHttpRequestDescriptor.HttpRequestData.Method;
-
- if (string.IsNullOrEmpty(httpMethod))
+ if (string.IsNullOrEmpty(signedHttpRequestDescriptor.HttpRequestData.Method))
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor.HttpRequestData.Method));
+ var httpMethod = signedHttpRequestDescriptor.HttpRequestData.Method;
if (!httpMethod.ToUpperInvariant().Equals(httpMethod))
throw LogHelper.LogExceptionMessage(new SignedHttpRequestCreationException(LogHelper.FormatInvariant(LogMessages.IDX23002, LogHelper.MarkAsNonPII(httpMethod))));
- payload.Add(SignedHttpRequestClaimTypes.M, httpMethod);
+ writer.WriteString(SignedHttpRequestClaimTypes.M, httpMethod);
}
///
- /// Adds the 'u' claim to the .
+ /// Adds the 'u' claim to the .
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
///
/// This method will be executed only if is set to true.
///
- internal virtual void AddUClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ internal virtual void AddUClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- var httpRequestUri = signedHttpRequestDescriptor.HttpRequestData.Uri;
-
- if (httpRequestUri == null)
+ if (signedHttpRequestDescriptor.HttpRequestData.Uri == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor.HttpRequestData.Uri));
- if (!httpRequestUri.IsAbsoluteUri)
- throw LogHelper.LogExceptionMessage(new SignedHttpRequestCreationException(LogHelper.FormatInvariant(LogMessages.IDX23001, httpRequestUri.OriginalString)));
+ if (!signedHttpRequestDescriptor.HttpRequestData.Uri.IsAbsoluteUri)
+ throw LogHelper.LogExceptionMessage(new SignedHttpRequestCreationException(LogHelper.FormatInvariant(LogMessages.IDX23001, signedHttpRequestDescriptor.HttpRequestData.Uri.OriginalString)));
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3
// u claim: The HTTP URL host component as a JSON string. This MAY include the port separated from the host by a colon in host:port format.
// Including the port if it not the default port for the httpRequestUri scheme.
- var httpUrlHostComponent = httpRequestUri.Host;
- if (!httpRequestUri.IsDefaultPort)
- httpUrlHostComponent = $"{httpUrlHostComponent}:{httpRequestUri.Port}";
+ string httpUrlHostComponent = signedHttpRequestDescriptor.HttpRequestData.Uri.Host;
+ if (!signedHttpRequestDescriptor.HttpRequestData.Uri.IsDefaultPort)
+ httpUrlHostComponent = $"{httpUrlHostComponent}:{signedHttpRequestDescriptor.HttpRequestData.Uri.Port}";
- payload.Add(SignedHttpRequestClaimTypes.U, httpUrlHostComponent);
+ writer.WriteString(SignedHttpRequestClaimTypes.U, httpUrlHostComponent);
}
///
- /// Adds the 'm' claim to the .
+ /// Adds the 'p' claim to the .
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
///
/// This method will be executed only if is set to true.
///
- internal virtual void AddPClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ internal virtual void AddPClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- var httpRequestUri = signedHttpRequestDescriptor.HttpRequestData.Uri;
-
- if (httpRequestUri == null)
+ if (signedHttpRequestDescriptor.HttpRequestData.Uri == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor.HttpRequestData.Uri));
- httpRequestUri = EnsureAbsoluteUri(httpRequestUri);
+ Uri httpRequestUri = EnsureAbsoluteUri(signedHttpRequestDescriptor.HttpRequestData.Uri);
- payload.Add(SignedHttpRequestClaimTypes.P, httpRequestUri.AbsolutePath);
+ writer.WriteString(SignedHttpRequestClaimTypes.P, httpRequestUri.AbsolutePath);
}
///
- /// Adds the 'q' claim to the .
+ /// Adds the 'q' claim to the .
///
- /// HttpRequest payload represented as a .
+ ///
/// A structure that wraps parameters needed for SignedHttpRequest creation.
///
/// This method will be executed only if is set to true.
- ///
- internal virtual void AddQClaim(Dictionary payload, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
+ ///
+ internal virtual void AddQClaim(ref Utf8JsonWriter writer, SignedHttpRequestDescriptor signedHttpRequestDescriptor)
{
- if (payload == null)
- throw LogHelper.LogArgumentNullException(nameof(payload));
-
- var httpRequestUri = signedHttpRequestDescriptor.HttpRequestData.Uri;
-
- if (httpRequestUri == null)
+ if (signedHttpRequestDescriptor.HttpRequestData.Uri == null)
throw LogHelper.LogArgumentNullException(nameof(signedHttpRequestDescriptor.HttpRequestData.Uri));
- httpRequestUri = EnsureAbsoluteUri(httpRequestUri);
- var sanitizedQueryParams = SanitizeQueryParams(httpRequestUri);
+ Uri httpRequestUri = EnsureAbsoluteUri(signedHttpRequestDescriptor.HttpRequestData.Uri);
+ IDictionary sanitizedQueryParams = SanitizeQueryParams(httpRequestUri);
StringBuilder stringBuffer = new StringBuilder();
- List queryParamNameList = new List();
try
{
+ writer.WriteStartArray(SignedHttpRequestClaimTypes.Q);
+ writer.WriteStartArray();
var firstQueryParam = true;
foreach (var queryParam in sanitizedQueryParams)
{
@@ -302,13 +322,15 @@ internal virtual void AddQClaim(Dictionary payload, SignedHttpRe
stringBuffer.Append("&");
stringBuffer.Append(queryParam.Key).Append('=').Append(queryParam.Value);
-
- queryParamNameList.Add(queryParam.Key);
firstQueryParam = false;
+
+ writer.WriteStringValue(queryParam.Key);
}
+ writer.WriteEndArray();
var base64UrlEncodedHash = CalculateBase64UrlEncodedHash(stringBuffer.ToString());
- payload.Add(SignedHttpRequestClaimTypes.Q, new List