From 9eae2a4da40ed99463aca44e58c65c9053f45ac0 Mon Sep 17 00:00:00 2001 From: Ge Date: Thu, 27 Feb 2025 04:57:13 +0800 Subject: [PATCH] Add Alibaba Cloud (Aliyun) to the list of supported providers --- ...tClientWebIntegrationHandlers.Discovery.cs | 24 ++++++++++++++--- ...ctClientWebIntegrationHandlers.Exchange.cs | 20 +++++++------- .../OpenIddictClientWebIntegrationHandlers.cs | 23 +++++++++++++--- ...penIddictClientWebIntegrationProviders.xml | 27 +++++++++++++++++++ 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs index a36861c86..ec803484e 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs @@ -108,8 +108,9 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) // types is amended to include the known supported types for the providers that require it. if (context.Registration.ProviderType is - ProviderTypes.Apple or ProviderTypes.FaceIt or - ProviderTypes.LinkedIn or ProviderTypes.QuickBooksOnline) + ProviderTypes.AlibabaCloud or ProviderTypes.Apple or + ProviderTypes.FaceIt or ProviderTypes.LinkedIn or + ProviderTypes.QuickBooksOnline) { context.Configuration.GrantTypesSupported.Add(GrantTypes.AuthorizationCode); context.Configuration.GrantTypesSupported.Add(GrantTypes.RefreshToken); @@ -250,7 +251,8 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) // While it is a recommended node, these providers don't include "scopes_supported" in their // configuration and thus are treated as OAuth 2.0-only providers by the OpenIddict client. // To avoid that, the "openid" scope is manually added to indicate OpenID Connect is supported. - else if (context.Registration.ProviderType is ProviderTypes.EpicGames or ProviderTypes.Xero or ProviderTypes.EveOnline) + else if (context.Registration.ProviderType is + ProviderTypes.EpicGames or ProviderTypes.EveOnline or ProviderTypes.Xero) { context.Configuration.ScopesSupported.Add(Scopes.OpenId); } @@ -283,6 +285,20 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) throw new ArgumentNullException(nameof(context)); } + // Alibaba Cloud doesn't document whether sending client credentials using basic authentication + // is supported and doesn't return a "token_endpoint_auth_methods_supported" nor a + // "revocation_endpoint_auth_methods_supported" node containing alternative authentication + // methods, making basic authentication the default. While both token and revocation requests + // currently work, "client_secret_post" is manually added here to avoid potential issues. + if (context.Registration.ProviderType is ProviderTypes.AlibabaCloud) + { + context.Configuration.TokenEndpointAuthMethodsSupported.Add( + ClientAuthenticationMethods.ClientSecretPost); + + context.Configuration.RevocationEndpointAuthMethodsSupported.Add( + ClientAuthenticationMethods.ClientSecretPost); + } + // Apple implements a non-standard client authentication method for its endpoints that // is inspired by the standard private_key_jwt method but doesn't use the standard // client_assertion/client_assertion_type parameters. Instead, the client assertion @@ -290,7 +306,7 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) // is the same as private_key_jwt, the configuration is amended to assume Apple supports // private_key_jwt and an event handler is responsible for populating the client_secret // parameter using the client assertion once it has been generated by OpenIddict. - if (context.Registration.ProviderType is ProviderTypes.Apple) + else if (context.Registration.ProviderType is ProviderTypes.Apple) { context.Configuration.RevocationEndpointAuthMethodsSupported.Add( ClientAuthenticationMethods.PrivateKeyJwt); diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs index 0e2dcab11..a8c9211f1 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs @@ -357,23 +357,23 @@ public ValueTask HandleAsync(ExtractTokenResponseContext context) context.Response.RefreshToken = null; } + // Note: Alibaba Cloud and Exact Online returns a non-standard "expires_in" + // parameter formatted as a string instead of a numeric type. + if (context.Registration.ProviderType is ProviderTypes.AlibabaCloud or ProviderTypes.ExactOnline && + long.TryParse((string?) context.Response[Parameters.ExpiresIn], + NumberStyles.Integer, CultureInfo.InvariantCulture, out long value)) + { + context.Response.ExpiresIn = value; + } + // 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) + else if (context.Registration.ProviderType is ProviderTypes.Deezer) { context.Response[Parameters.ExpiresIn] = context.Response["expires"]; context.Response["expires"] = null; } - // Note: Exact Online returns a non-standard "expires_in" - // parameter formatted as a string instead of a numeric type. - else if (context.Registration.ProviderType is ProviderTypes.ExactOnline && - long.TryParse((string?) context.Response[Parameters.ExpiresIn], - NumberStyles.Integer, CultureInfo.InvariantCulture, out long value)) - { - context.Response.ExpiresIn = value; - } - // Note: Huawei returns a non-standard "error" parameter as a numeric value, which is not allowed // by OpenIddict (that requires a string). Huawei also returns a non-standard "sub_error" parameter // that contains additional error information, with which the error code can demonstrate a specific diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs index 16895ff7c..c9d73c501 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs @@ -1853,13 +1853,28 @@ public ValueTask HandleAsync(ProcessChallengeContext context) throw new ArgumentNullException(nameof(context)); } - // Active Directory Federation Services allows sending a custom "resource" + // Active Directory Federation Services allows sending an optional custom "resource" // parameter to define what API resources the access token will give access to. if (context.Registration.ProviderType is ProviderTypes.ActiveDirectoryFederationServices) { var settings = context.Registration.GetActiveDirectoryFederationServicesSettings(); - context.Request["resource"] = settings.Resource; + if (!string.IsNullOrEmpty(settings.Resource)) + { + context.Request.Resources = [settings.Resource]; + } + } + + // By default, Alibaba Cloud doesn't return a refresh token for native applications but allows sending an + // "access_type" parameter to retrieve one (but it is only returned during the first authorization dance). + // The documentation also indicates the "prompt" parameter is supported but not required, + // which can be set to "admin_consent" to force the display of the authorization page + if (context.Registration.ProviderType is ProviderTypes.AlibabaCloud) + { + var settings = context.Registration.GetAlibabaCloudSettings(); + + context.Request["access_type"] = settings.AccessType; + context.Request.Prompt = settings.Prompt; } // Atlassian requires sending an "audience" parameter (by default, "api.atlassian.com"). @@ -1899,7 +1914,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) var settings = context.Registration.GetHuaweiSettings(); context.Request["access_type"] = settings.AccessType; - context.Request["display"] = settings.Display; + context.Request.Display = settings.Display; } // By default, MusicBrainz doesn't return a refresh token but allows sending an "access_type" @@ -1956,7 +1971,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) { var settings = context.Registration.GetWeiboSettings(); - context.Request["display"] = settings.Display; + context.Request.Display = settings.Display; context.Request["forcelogin"] = settings.ForceLogin; context.Request["language"] = settings.Language; } diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index 1749cc5d6..95b1eb99e 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -74,6 +74,33 @@ + + + + + + + + + + + + + +