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 @@
+
+
+
+