From 7aac1028306cf0fefb6d5ab9e3556ce2edea4509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 23 Feb 2024 12:42:00 +0100 Subject: [PATCH] Add Dailymotion to the list of supported providers --- .../OpenIddictClientAspNetCoreExtensions.cs | 6 +- ...penIddictClientDataProtectionExtensions.cs | 6 +- .../OpenIddictClientOwinExtensions.cs | 6 +- ...IddictClientSystemIntegrationExtensions.cs | 6 +- ...OpenIddictClientSystemNetHttpExtensions.cs | 6 +- ...penIddictClientWebIntegrationExtensions.cs | 6 +- ...ctClientWebIntegrationHandlers.Exchange.cs | 11 ++++ .../OpenIddictClientWebIntegrationHandlers.cs | 36 ++++++++---- ...penIddictClientWebIntegrationProviders.xml | 55 +++++++++++++++++++ ...penIddictClientWebIntegrationProviders.xsd | 1 + .../OpenIddictServerAspNetCoreExtensions.cs | 6 +- ...penIddictServerDataProtectionExtensions.cs | 6 +- .../OpenIddictServerOwinExtensions.cs | 6 +- ...penIddictValidationAspNetCoreExtensions.cs | 6 +- ...ddictValidationDataProtectionExtensions.cs | 6 +- .../OpenIddictValidationOwinExtensions.cs | 6 +- ...ctValidationServerIntegrationExtensions.cs | 6 +- ...IddictValidationSystemNetHttpExtensions.cs | 6 +- .../Primitives/OpenIddictExtensionsTests.cs | 2 +- 19 files changed, 136 insertions(+), 53 deletions(-) diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreExtensions.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreExtensions.cs index 158fd791..5f6b5370 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreExtensions.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreExtensions.cs @@ -48,14 +48,14 @@ public static class OpenIddictClientAspNetCoreExtensions // Register the option initializer used by the OpenIddict ASP.NET Core client integration services. // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton, OpenIddictClientAspNetCoreConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientAspNetCoreConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientAspNetCoreConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientAspNetCoreConfiguration>() - }); + ]); return new OpenIddictClientAspNetCoreBuilder(builder.Services); } diff --git a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionExtensions.cs b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionExtensions.cs index 8c99bc60..e02df3c7 100644 --- a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionExtensions.cs +++ b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionExtensions.cs @@ -40,11 +40,11 @@ public static class OpenIddictClientDataProtectionExtensions builder.Services.TryAddSingleton(); // Note: TryAddEnumerable() is used here to ensure the initializers are registered only once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton, OpenIddictClientDataProtectionConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientDataProtectionConfiguration>() - }); + ]); return new OpenIddictClientDataProtectionBuilder(builder.Services); } diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs index 6335092d..3e6f5e2e 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs @@ -49,11 +49,11 @@ public static class OpenIddictClientOwinExtensions // Register the option initializer used by the OpenIddict OWIN client integration services. // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton, OpenIddictClientOwinConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientOwinConfiguration>() - }); + ]); return new OpenIddictClientOwinBuilder(builder.Services); } diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs index a86ca4ce..ef11230f 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs @@ -72,8 +72,8 @@ public static class OpenIddictClientSystemIntegrationExtensions // Register the option initializer and the background service used by the OpenIddict client system integration services. // Note: TryAddEnumerable() is used here to ensure the initializers and the background service are only registered once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton(), ServiceDescriptor.Singleton(), @@ -81,7 +81,7 @@ public static class OpenIddictClientSystemIntegrationExtensions ServiceDescriptor.Singleton, OpenIddictClientSystemIntegrationConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientSystemIntegrationConfiguration>() - }); + ]); return new OpenIddictClientSystemIntegrationBuilder(builder.Services); } diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpExtensions.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpExtensions.cs index 2e02a87e..4c72d781 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpExtensions.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpExtensions.cs @@ -40,11 +40,11 @@ public static class OpenIddictClientSystemNetHttpExtensions builder.Services.TryAddSingleton(); // Note: TryAddEnumerable() is used here to ensure the initializers are registered only once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton, OpenIddictClientSystemNetHttpConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientSystemNetHttpConfiguration>() - }); + ]); return new OpenIddictClientSystemNetHttpBuilder(builder.Services); } diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationExtensions.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationExtensions.cs index 3d317357..783d54e2 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationExtensions.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationExtensions.cs @@ -39,11 +39,11 @@ public static partial class OpenIddictClientWebIntegrationExtensions .Select(descriptor => descriptor.ServiceDescriptor)); // Note: TryAddEnumerable() is used here to ensure the initializers are registered only once. - builder.Services.TryAddEnumerable(new[] - { + builder.Services.TryAddEnumerable( + [ ServiceDescriptor.Singleton, OpenIddictClientWebIntegrationConfiguration>(), ServiceDescriptor.Singleton, OpenIddictClientWebIntegrationConfiguration>() - }); + ]); // Note: the IPostConfigureOptions service responsible for populating // the client registrations MUST be registered before OpenIddictClientConfiguration to ensure diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs index 81dbcc9f..d57126bc 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs @@ -11,6 +11,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text; +using System.Text.Json; using OpenIddict.Extensions; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpConstants; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlerFilters; @@ -356,6 +357,16 @@ public static partial class OpenIddictClientWebIntegrationHandlers return default; } + // Note: when using the client credentials grant, Dailymotion returns a "refresh_token" + // node with a JSON null value, which isn't allowed by OpenIddict (that requires a string). + // + // To work around that, the "refresh_token" node is removed when it is set to a null value . + if (context.Registration.ProviderType is ProviderTypes.Dailymotion && (JsonElement?) + context.Response[Parameters.RefreshToken] is { ValueKind: JsonValueKind.Null }) + { + context.Response.RefreshToken = null; + } + // Note: Deezer doesn't return a standard "expires_in" parameter // but returns an equivalent "expires" integer parameter instead. if (context.Registration.ProviderType is ProviderTypes.Deezer) diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs index 2fe301ff..6aa8be65 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs @@ -686,6 +686,12 @@ public static partial class OpenIddictClientWebIntegrationHandlers context.UserinfoEndpoint = context.Registration.ProviderType switch { + // Dailymotion's userinfo endpoint requires sending the user identifier in the URI path. + ProviderTypes.Dailymotion when (string?) context.TokenResponse?["uid"] is string identifier + => OpenIddictHelpers.CreateAbsoluteUri( + left : new Uri("https://api.dailymotion.com/user", UriKind.Absolute), + right: new Uri(identifier, UriKind.Relative)), + // HubSpot doesn't have a static userinfo endpoint but allows retrieving basic information // by using an access token info endpoint that requires sending the token in the URI path. ProviderTypes.HubSpot when @@ -852,10 +858,20 @@ public static partial class OpenIddictClientWebIntegrationHandlers Debug.Assert(context.UserinfoRequest is not null, SR.GetResourceString(SR.ID4008)); + // Dailymotion 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. + if (context.Registration.ProviderType is ProviderTypes.Dailymotion) + { + var settings = context.Registration.GetDailymotionSettings(); + + context.UserinfoRequest["fields"] = string.Join(",", settings.UserFields); + } + // Facebook 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. - if (context.Registration.ProviderType is ProviderTypes.Facebook) + else if (context.Registration.ProviderType is ProviderTypes.Facebook) { var settings = context.Registration.GetFacebookSettings(); @@ -1084,9 +1100,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers context.MergedPrincipal.SetClaim(ClaimTypes.Name, issuer: issuer, value: context.Registration.ProviderType switch { // These providers return the username as a custom "username" node: - ProviderTypes.ArcGisOnline or ProviderTypes.Discord or ProviderTypes.DeviantArt or - ProviderTypes.Lichess or ProviderTypes.Mixcloud or ProviderTypes.Trakt or - ProviderTypes.WordPress + ProviderTypes.ArcGisOnline or ProviderTypes.Dailymotion or ProviderTypes.Discord or + ProviderTypes.DeviantArt or ProviderTypes.Lichess or ProviderTypes.Mixcloud or + ProviderTypes.Trakt or ProviderTypes.WordPress => (string?) context.UserinfoResponse?["username"], // Basecamp and Harvest don't return a username so one is created using the "first_name" and "last_name" nodes: @@ -1166,12 +1182,12 @@ public static partial class OpenIddictClientWebIntegrationHandlers => (string?) context.UserinfoResponse?["username"], // These providers return the user identifier as a custom "id" node: - ProviderTypes.Basecamp or ProviderTypes.Deezer or ProviderTypes.Discord or - ProviderTypes.Facebook or ProviderTypes.GitHub or ProviderTypes.Harvest or - ProviderTypes.Kroger or ProviderTypes.Lichess or ProviderTypes.Nextcloud or - ProviderTypes.Patreon or ProviderTypes.Reddit or ProviderTypes.Smartsheet or - ProviderTypes.Spotify or ProviderTypes.SubscribeStar or ProviderTypes.Twitter or - ProviderTypes.Zoom + ProviderTypes.Basecamp or ProviderTypes.Dailymotion or ProviderTypes.Deezer or + ProviderTypes.Discord or ProviderTypes.Facebook or ProviderTypes.GitHub or + ProviderTypes.Harvest or ProviderTypes.Kroger or ProviderTypes.Lichess or + ProviderTypes.Nextcloud or ProviderTypes.Patreon or ProviderTypes.Reddit or + ProviderTypes.Smartsheet or ProviderTypes.Spotify or ProviderTypes.SubscribeStar or + ProviderTypes.Twitter or ProviderTypes.Zoom => (string?) context.UserinfoResponse?["id"], // Bitbucket returns the user identifier as a custom "uuid" node: diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index 33c27983..b3ad8e9d 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -263,6 +263,61 @@ Description="The User Pool ID" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +