diff --git a/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs b/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs index aa044122..ec85e49c 100644 --- a/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs +++ b/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs @@ -237,6 +237,46 @@ public partial class OpenIddictClientWebIntegrationConfiguration {{~ end ~}} {{~ end ~}} + {{~ for environment in provider.environments ~}} + if (settings.Environment is OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }}) + { + {{~ for scope in environment.scopes ~}} + {{~ if scope.required ~}} + settings.Scopes.Add(""{{ scope.name }}""); + {{~ end ~}} + + {{~ if scope.default ~}} + if (settings.Scopes.Count is 0) + { + settings.Scopes.Add(""{{ scope.name }}""); + } + {{~ end ~}} + {{~ end ~}} + } + {{~ end ~}} + + {{~ for setting in provider.settings ~}} + {{~ if setting.default_value ~}} + if (string.IsNullOrEmpty(settings.{{ setting.name }})) + { + settings.{{ setting.name }} = ""{{ setting.default_value }}""; + } + {{~ end ~}} + + {{~ for item in setting.collection_items ~}} + {{~ if item.required ~}} + settings.{{ setting.name }}.Add(""{{ item.value }}""); + {{~ end ~}} + + {{~ if item.default ~}} + if (settings.{{ setting.name }}.Count is 0) + { + settings.{{ setting.name }}.Add(""{{ item.value }}""); + } + {{~ end ~}} + {{~ end ~}} + {{~ end ~}} + var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings { CaseSensitivity = CaseSensitivityType.CaseInsensitive @@ -353,39 +393,6 @@ public partial class OpenIddictClientWebIntegrationConfiguration registration.Scopes.UnionWith(settings.Scopes); - {{~ for environment in provider.environments ~}} - if (settings.Environment is OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }}) - { - {{~ for scope in environment.scopes ~}} - {{~ if scope.required ~}} - registration.Scopes.Add(""{{ scope.name }}""); - {{~ end ~}} - - {{~ if scope.default ~}} - if (registration.Scopes.Count is 0) - { - registration.Scopes.Add(""{{ scope.name }}""); - } - {{~ end ~}} - {{~ end ~}} - } - {{~ end ~}} - - {{~ for setting in provider.settings ~}} - {{~ for item in setting.collection_items ~}} - {{~ if item.required ~}} - settings.{{ setting.name }}.Add(""{{ item.value }}""); - {{~ end ~}} - - {{~ if item.default ~}} - if (settings.{{ setting.name }}.Count is 0) - { - settings.{{ setting.name }}.Add(""{{ item.value }}""); - } - {{~ end ~}} - {{~ end ~}} - {{~ end ~}} - options.Registrations.Add(registration); } } @@ -481,6 +488,8 @@ public partial class OpenIddictClientWebIntegrationConfiguration EncryptionAlgorithm = (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value"), SigningAlgorithm = (string?) setting.Element("SigningAlgorithm")?.Attribute("Value"), + DefaultValue = (string?) setting.Attribute("DefaultValue"), + CollectionItems = setting.Elements("CollectionItem").Select(item => new { Value = (string) item.Attribute("Value"), diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs index 2ed614a6..836b2e53 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs @@ -19,7 +19,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers * Configuration response handling: */ AmendIssuer.Descriptor, - AmendClientAuthenticationMethods.Descriptor); + AmendClientAuthenticationMethods.Descriptor, + AmendCodeChallengeMethods.Descriptor); /// /// Contains the logic responsible for amending the issuer for the providers that require it. @@ -52,7 +53,11 @@ public static partial class OpenIddictClientWebIntegrationHandlers // is replaced by this handler to always use "https://login.microsoftonline.com/common/v2.0". if (context.Registration.GetProviderName() is Providers.Microsoft) { - context.Response[Metadata.Issuer] = "https://login.microsoftonline.com/common/v2.0"; + var settings = context.Registration.GetMicrosoftSettings(); + if (string.Equals(settings.Tenant, "common", StringComparison.OrdinalIgnoreCase)) + { + context.Response[Metadata.Issuer] = "https://login.microsoftonline.com/common/v2.0"; + } } return default; @@ -60,8 +65,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers } /// - /// Contains the logic responsible for amending the client - /// authentication methods for the providers that require it. + /// Contains the logic responsible for amending the supported + /// client authentication methods for the providers that require it. /// public class AmendClientAuthenticationMethods : IOpenIddictClientHandler { @@ -99,5 +104,43 @@ public static partial class OpenIddictClientWebIntegrationHandlers return default; } } + + /// + /// Contains the logic responsible for amending the supported + /// code challenge methods for the providers that require it. + /// + public class AmendCodeChallengeMethods : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(ExtractCodeChallengeMethods.Descriptor.Order + 500) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(HandleConfigurationResponseContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // Microsoft Account supports both "plain" and "S256" code challenge methods but + // don't list them in the server configuration metadata. To ensure the OpenIddict + // client uses Proof Key for Code Exchange for the Microsoft provider, the 2 methods + // are manually added to the list of supported code challenge methods by this handler. + if (context.Registration.GetProviderName() is Providers.Microsoft) + { + context.Configuration.CodeChallengeMethodsSupported.Add(CodeChallengeMethods.Plain); + context.Configuration.CodeChallengeMethodsSupported.Add(CodeChallengeMethods.Sha256); + } + + return default; + } + } } } diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs index f2a9d25d..ba509c1a 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs @@ -52,11 +52,14 @@ public static partial class OpenIddictClientWebIntegrationHandlers context.TokenValidationParameters.ValidateIssuer = context.Registration.GetProviderName() switch { - // While the Microsoft Account provider uses the "common" tenant, the issued tokens include - // a dynamic issuer claim corresponding to the tenant instance that is associated with - // the client application. Since the tenant cannot be inferred when targeting the common - // tenant, issuer validation is manually disabled for the Microsoft Account provider. - Providers.Microsoft => false, + // When the Microsoft Account provider is configured to use the "common" tenant, + // the returned tokens include a dynamic issuer claim corresponding to the tenant + // that is associated with the client application. Since the tenant cannot be + // inferred when targeting the common tenant instance, issuer validation is disabled. + Providers.Microsoft when string.Equals( + context.Registration.GetMicrosoftSettings().Tenant, + "common", StringComparison.OrdinalIgnoreCase) + => false, _ => context.TokenValidationParameters.ValidateIssuer }; diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index 515f6b30..c187d4dd 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -26,7 +26,17 @@ - + + + + + diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd index 0af782b9..0f16703d 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd @@ -375,6 +375,16 @@ + + + The default value used if no value was explictly set by the user. + + + + + + + The setting description.