diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs index dd20ffe4..2ed614a6 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs @@ -18,8 +18,47 @@ public static partial class OpenIddictClientWebIntegrationHandlers /* * Configuration response handling: */ + AmendIssuer.Descriptor, AmendClientAuthenticationMethods.Descriptor); + /// + /// Contains the logic responsible for amending the issuer for the providers that require it. + /// + public class AmendIssuer : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(ValidateIssuer.Descriptor.Order - 500) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(HandleConfigurationResponseContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // Note: the server configuration metadata returned by the Microsoft Account "common" tenant + // uses "https://login.microsoftonline.com/{tenantid}/v2.0" as the issuer to indicate that + // the issued identity tokens will have a dynamic issuer claim whose value will be resolved + // based on the client identity. As required by RFC8414, OpenIddict would automatically reject + // such responses as the issuer wouldn't match the expected value. To work around that, the issuer + // 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"; + } + + return default; + } + } + /// /// Contains the logic responsible for amending the client /// authentication methods for the providers that require it. diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs new file mode 100644 index 00000000..f2a9d25d --- /dev/null +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System.Collections.Immutable; +using static OpenIddict.Client.OpenIddictClientHandlers.Protection; +using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; + +namespace OpenIddict.Client.WebIntegration; + +public static partial class OpenIddictClientWebIntegrationHandlers +{ + public static class Protection + { + public static ImmutableArray DefaultHandlers { get; } = ImmutableArray.Create( + /* + * Token validation: + */ + AmendTokenValidationParameters.Descriptor); + + /// + /// Contains the logic responsible for amending the token validation parameters for the providers that require it. + /// + public class AmendTokenValidationParameters : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(ResolveTokenValidationParameters.Descriptor.Order + 500) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // Note: the client registration may be null (e.g when validating a state token). + // In this case, don't amend the default token validation parameters. + if (context.Registration is null) + { + return default; + } + + 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, + + _ => context.TokenValidationParameters.ValidateIssuer + }; + + return default; + } + } + } +} diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs index 639b4749..f6c95415 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs @@ -101,10 +101,17 @@ public static partial class OpenIddictClientWebIntegrationHandlers // logic from mapping the parameters to CLR claims. To work around that, this handler // is responsible for extracting the nested payload and replacing the userinfo response. - if (context.Registration.GetProviderName() is Providers.Twitter) + var parameter = context.Registration.GetProviderName() switch + { + Providers.Twitter => "data", + + _ => null + }; + + if (!string.IsNullOrEmpty(parameter)) { - context.Response = new OpenIddictResponse(context.Response["data"]?.GetNamedParameters() ?? - throw new InvalidOperationException(SR.FormatID0334("data"))); + context.Response = new OpenIddictResponse(context.Response[parameter]?.GetNamedParameters() ?? + throw new InvalidOperationException(SR.FormatID0334(parameter))); } return default; diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs index ba1448a0..8062e730 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs @@ -31,6 +31,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers FormatNonStandardScopeParameter.Descriptor) .AddRange(Discovery.DefaultHandlers) .AddRange(Exchange.DefaultHandlers) + .AddRange(Protection.DefaultHandlers) .AddRange(Userinfo.DefaultHandlers); /// diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index 34d37569..515f6b30 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -25,6 +25,10 @@ + + + +