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;
}
}