diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index a3917a6e..bf0fd2e4 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1299,7 +1299,7 @@ Alternatively, you can disable the token storage feature by calling 'services.Ad The '{0}' provider settings cannot be resolved from the event context. Make sure the provider was correctly registered using 'services.AddOpenIddict().AddClient().UseWebProviders().Add{0}()'. - The '{0}' node cannot be extracted from the response. + The '{0}' node cannot be extracted from the response or is not of the expected type. The username cannot be null or empty. diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs index f12c7c97..6e310563 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs @@ -6,7 +6,9 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Text.Json; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers; +using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers.Userinfo; using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; namespace OpenIddict.Client.WebIntegration; @@ -19,7 +21,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers /* * Userinfo request preparation: */ - AttachNonStandardFieldParameter.Descriptor, + AttachAdditionalParameters.Descriptor, /* * Userinfo response extraction: @@ -27,16 +29,16 @@ public static partial class OpenIddictClientWebIntegrationHandlers UnwrapUserinfoResponse.Descriptor); /// - /// Contains the logic responsible for attaching non-standard field parameters for the providers that require it. + /// Contains the logic responsible for attaching additional parameters for the providers that require it. /// - public class AttachNonStandardFieldParameter : IOpenIddictClientHandler + public class AttachAdditionalParameters : IOpenIddictClientHandler { /// /// Gets the default descriptor definition assigned to this handler. /// public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() - .UseSingletonHandler() + .UseSingletonHandler() .SetOrder(PrepareGetHttpRequest.Descriptor.Order - 500) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -51,15 +53,13 @@ public static partial class OpenIddictClientWebIntegrationHandlers Debug.Assert(context.Request is not null, SR.GetResourceString(SR.ID4008)); - // Some providers are known to limit the number of fields returned by their userinfo endpoint - // but allow returning additional information using a special parameter (generally called "fields") - // that determines what fields will be returned as part of the userinfo response. This handler is - // responsible for resolving the fields from the provider settings and attaching them to the request. - if (context.Registration.ProviderName is Providers.Twitter) { var options = context.Registration.GetTwitterOptions(); + // Twitter limits the number of fields returned by the userinfo endpoint + // but allows returning additional information using special parameters that + // determine what fields will be returned as part of the userinfo response. context.Request["expansions"] = string.Join(",", options.Expansions); context.Request["tweet.fields"] = string.Join(",", options.TweetFields); context.Request["user.fields"] = string.Join(",", options.UserFields); @@ -100,19 +100,16 @@ 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. - var parameter = context.Registration.ProviderName switch + context.Response = context.Registration.ProviderName switch { - Providers.Twitter => "data", + // Twitter uses a nested object. + Providers.Twitter => (JsonElement?) context.Response["data"] + is { ValueKind: JsonValueKind.Object } element ? + new(element) : throw new InvalidOperationException(SR.FormatID0334("data")), - _ => null + _ => context.Response }; - if (!string.IsNullOrEmpty(parameter)) - { - context.Response = new OpenIddictResponse(context.Response[parameter]?.GetNamedParameters() ?? - throw new InvalidOperationException(SR.FormatID0334(parameter))); - } - return default; } }