diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
index 0756ba75..57216ce3 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
@@ -19,6 +19,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
/*
* Authentication processing:
*/
+ HandleNonStandardFrontchannelErrorResponse.Descriptor,
AttachNonStandardClientAssertionTokenClaims.Descriptor,
AttachTokenRequestNonStandardClientCredentials.Descriptor,
AttachAdditionalUserinfoRequestParameters.Descriptor,
@@ -32,6 +33,61 @@ public static partial class OpenIddictClientWebIntegrationHandlers
.AddRange(Protection.DefaultHandlers)
.AddRange(Userinfo.DefaultHandlers);
+ ///
+ /// Contains the logic responsible for handling non-standard
+ /// authorization errors for the providers that require it.
+ ///
+ public class HandleNonStandardFrontchannelErrorResponse : IOpenIddictClientHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictClientHandlerDescriptor Descriptor { get; }
+ = OpenIddictClientHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(HandleFrontchannelErrorResponse.Descriptor.Order - 500)
+ .SetType(OpenIddictClientHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(ProcessAuthenticationContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // Note: some providers are known to return non-standard errors.
+ // To normalize the set of errors handled by the OpenIddict client,
+ // the non-standard errors are mapped to their standard equivalent.
+ //
+ // Errors that are not handled here will be automatically handled
+ // by the standard handler present in the core OpenIddict client.
+
+ if (context.Registration.ProviderName is Providers.LinkedIn)
+ {
+ var error = (string?) context.Request[Parameters.Error];
+ if (string.IsNullOrEmpty(error))
+ {
+ return default;
+ }
+
+ if (string.Equals(error, "user_cancelled_authorize", StringComparison.Ordinal) ||
+ string.Equals(error, "user_cancelled_login", StringComparison.Ordinal))
+ {
+ context.Reject(
+ error: Errors.AccessDenied,
+ description: SR.GetResourceString(SR.ID2149),
+ uri: SR.FormatID8000(SR.ID2149));
+
+ return default;
+ }
+ }
+
+ return default;
+ }
+ }
+
///
/// Contains the logic responsible for amending the client
/// assertion methods for the providers that require it.
@@ -148,7 +204,17 @@ public static partial class OpenIddictClientWebIntegrationHandlers
Debug.Assert(context.UserinfoRequest is not null, SR.GetResourceString(SR.ID4008));
- if (context.Registration.ProviderName is Providers.StackExchange)
+ if (context.Registration.ProviderName is Providers.LinkedIn)
+ {
+ var options = context.Registration.GetLinkedInOptions();
+
+ // By default, LinkedIn returns all the basic fields except the profile image.
+ // To retrieve the profile image, a projection parameter must be sent with
+ // all the parameters that should be returned from the userinfo endpoint.
+ context.UserinfoRequest["projection"] = string.Concat("(", string.Join(",", options.Fields), ")");
+ }
+
+ else if (context.Registration.ProviderName is Providers.StackExchange)
{
var options = context.Registration.GetStackExchangeOptions();
diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
index 6ba621ff..222d930b 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
@@ -25,6 +25,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+