Browse Source

Add Huawei to the list of supported providers

pull/2099/head
Ge 2 years ago
committed by GitHub
parent
commit
7194d58132
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 33
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Device.cs
  2. 26
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
  3. 37
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs
  4. 44
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
  5. 20
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml

33
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Device.cs

@ -5,6 +5,8 @@
*/
using System.Collections.Immutable;
using System.Text.Json;
using OpenIddict.Extensions;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
namespace OpenIddict.Client.WebIntegration;
@ -57,6 +59,37 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Response["verification_url"] = null;
}
// Note: Huawei returns a non-standard "error" parameter as a numeric value, which is
// not allowed by OpenIddict (that requires a string). It also doesn't return a standard
// "verification_uri" parameter but returns a custom "verification_url" that serves the
// same purpose. Similarly, a custom "expire_in" parameter is used instead of "expires_in".
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
if ((JsonElement?) context.Response[Parameters.Error] is { ValueKind: JsonValueKind.Number })
{
context.Response[Parameters.Error] = Errors.InvalidRequest;
}
if (!string.IsNullOrEmpty(context.Response.UserCode) &&
Uri.TryCreate((string?) context.Response["verification_url"], UriKind.Absolute, out Uri? uri))
{
// Note: the user verification URI returned by Huawei points to an endpoint that always returns
// a JSON error when it is accessed without the "user_code" parameter attached. To ensure the
// user verification URI returned by the OpenIddict client service to the caller can be used
// as-is, both parameters are replaced to always include the user code in the query string.
context.Response[Parameters.VerificationUri] =
context.Response[Parameters.VerificationUriComplete] = OpenIddictHelpers.AddQueryStringParameter(
uri : uri,
name : Parameters.UserCode,
value: context.Response.UserCode).AbsoluteUri;
context.Response["verification_url"] = null;
}
context.Response[Parameters.ExpiresIn] = context.Response["expire_in"];
context.Response["expire_in"] = null;
}
return default;
}
}

26
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs

@ -144,6 +144,14 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Configuration.GrantTypesSupported.Add(GrantTypes.Implicit);
}
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
context.Configuration.GrantTypesSupported.Add(GrantTypes.AuthorizationCode);
context.Configuration.GrantTypesSupported.Add(GrantTypes.ClientCredentials);
context.Configuration.GrantTypesSupported.Add(GrantTypes.DeviceCode);
context.Configuration.GrantTypesSupported.Add(GrantTypes.RefreshToken);
}
else if (context.Registration.ProviderType is
ProviderTypes.DocuSign or ProviderTypes.Asana or ProviderTypes.Slack)
{
@ -355,6 +363,15 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ClientAuthenticationMethods.ClientSecretPost);
}
// Huawei doesn't support sending the client credentials using basic authentication when
// using the device authorization grant, making basic authentication the default authentication
// method. To work around this compliance issue, "client_secret_post" is manually added here.
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
context.Configuration.DeviceAuthorizationEndpointAuthMethodsSupported.Add(
ClientAuthenticationMethods.ClientSecretPost);
}
// LinkedIn doesn't support sending the client credentials using basic authentication but
// doesn't return a "token_endpoint_auth_methods_supported" node containing alternative
// authentication methods, making basic authentication the default authentication method.
@ -409,6 +426,15 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Registration.Issuer, "oidc/logout");
}
// While Huawei supports OpenID Connect discovery, the configuration
// document doesn't return the address of the device authorization endpoint.
// To work around that, the endpoint URI is manually added here.
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
context.Configuration.DeviceAuthorizationEndpoint =
new Uri("https://oauth-login.cloud.huawei.com/oauth2/v3/device/code", UriKind.Absolute);
}
// While it exposes a standard OpenID Connect userinfo endpoint, Orange France doesn't list it
// in its configuration document. To work around that, the endpoint URI is manually added here.
else if (context.Registration.ProviderType is ProviderTypes.OrangeFrance)

37
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs

@ -92,6 +92,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Request.GrantType = null;
}
// Huawei doesn't support the standard "urn:ietf:params:oauth:grant-type:device_code"
// grant type and requires using the non-standard "device_code" grant type instead.
// It also doesn't support the standard "device_code" device code parameter and
// requires using the non-standard "code" device code parameter instead.
if (context.GrantType is GrantTypes.DeviceCode &&
context.Registration.ProviderType is ProviderTypes.Huawei)
{
context.Request.GrantType = "device_code";
context.Request.Code = context.Request.DeviceCode;
context.Request.DeviceCode = null;
}
// World ID doesn't support the standard and mandatory redirect_uri parameter and returns
// a HTTP 500 response when specifying it in a grant_type=authorization_code token request.
//
@ -405,6 +417,31 @@ public static partial class OpenIddictClientWebIntegrationHandlers
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
// meaning. To work around that, the "error" parameter is replaced with a standard error code.
// When the error code is "1101", the sub-error code of "20411" indicates that the device code
// authorization request is still waiting for the user to access the authorization page; the
// sub-error code of "20412" indicates that the user has not performed the device code authorization;
// the sub-error code of "20414" indicates that the user has denied the device code authorization.
// For more information about the error codes, sub-error codes, and their meanings, see:
// https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/open-platform-error-0000001053869182#section6581130161218
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
context.Response[Parameters.Error] =
((long?) context.Response[Parameters.Error], (long?) context.Response["sub_error"]) switch
{
(1101, 20404) => Errors.ExpiredToken,
(1101, 20411 or 20412) => Errors.AuthorizationPending,
(1101, 20414) => Errors.AccessDenied,
(not null, _) => Errors.InvalidRequest,
_ => null,
};
}
// Note: Tumblr returns a non-standard "id_token: false" node that collides
// with the standard id_token parameter used in OpenID Connect. To ensure
// the response is not rejected, the "id_token" node is manually removed.

44
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs

@ -266,6 +266,32 @@ public static partial class OpenIddictClientWebIntegrationHandlers
}
}
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
var error = (long?) context.Request[Parameters.Error];
if (error is not null)
{
context.Reject(
error: error switch
{
1107 => Errors.AccessDenied,
_ => Errors.ServerError
},
description: error switch
{
1107 => SR.GetResourceString(SR.ID2149),
_ => SR.GetResourceString(SR.ID2152)
},
uri: error switch
{
1107 => SR.FormatID8000(SR.ID2149),
_ => SR.FormatID8000(SR.ID2152)
});
return default;
}
}
else if (context.Registration.ProviderType is ProviderTypes.LinkedIn)
{
var error = (string?) context.Request[Parameters.Error];
@ -601,6 +627,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.TokenRequest.RedirectUri = context.Registration.ProviderType switch
{
ProviderTypes.Deezer or
ProviderTypes.Huawei or
ProviderTypes.Mixcloud => OpenIddictHelpers.AddQueryStringParameter(
uri : new Uri(context.TokenRequest.RedirectUri, UriKind.Absolute),
name : Parameters.State,
@ -1189,6 +1216,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers
// FitBit returns the username as a custom "displayName" node:
ProviderTypes.Fitbit => (string?) context.UserinfoResponse?["displayName"],
// Huawei returns the username as a custom "display_name" in the backchannel identity token:
ProviderTypes.Huawei => context.BackchannelIdentityTokenPrincipal?.GetClaim("display_name"),
// HubSpot returns the username as a custom "user" node:
ProviderTypes.HubSpot => (string?) context.UserinfoResponse?["user"],
@ -1549,7 +1579,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
// Note: this workaround only works for providers that allow dynamic
// redirection URIs and implement a relaxed validation policy logic.
if (context.Registration.ProviderType is ProviderTypes.Deezer or ProviderTypes.Mixcloud)
if (context.Registration.ProviderType is ProviderTypes.Deezer or ProviderTypes.Huawei or ProviderTypes.Mixcloud)
{
context.Request.RedirectUri = OpenIddictHelpers.AddQueryStringParameter(
uri : new Uri(context.RedirectUri, UriKind.Absolute),
@ -1625,6 +1655,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Request["access_type"] = settings.AccessType;
}
// By default, Huawei doesn't return a refresh token 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 "display" parameter is supported but not required,
// which can be set to "touch" to adjust the authorization page display style for mobile apps.
else if (context.Registration.ProviderType is ProviderTypes.Huawei)
{
var settings = context.Registration.GetHuaweiSettings();
context.Request["access_type"] = settings.AccessType;
context.Request["display"] = settings.Display;
}
// By default, MusicBrainz doesn't return a refresh token but allows sending an "access_type"
// parameter to retrieve one (but it is only returned during the first authorization dance).
else if (context.Registration.ProviderType is ProviderTypes.MusicBrainz)

20
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml

@ -738,6 +738,26 @@
</Environment>
</Provider>
<!--
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██ ██ ██ ██ █ ▄▄▀██ ███ ██ ▄▄▄█▄ ▄█
██ ▄▄ ██ ██ █ ▀▀ ██ █ █ ██ ▄▄▄██ ██
██ ██ ██▄▀▀▄█ ██ ██▄▀▄▀▄██ ▀▀▀█▀ ▀█
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-->
<Provider Name="Huawei" Id="95a620d2-a362-47b0-b372-26fa9d41d20e"
Documentation="https://developer.huawei.com/consumer/en/doc/HMSCore-Guides/open-platform-oauth-0000001053629189">
<Environment Issuer="https://accounts.huawei.com/"
ConfigurationEndpoint="https://oauth-login.cloud.huawei.com/.well-known/openid-configuration" />
<Setting PropertyName="AccessType" ParameterName="type" Type="String" Required="false"
Description="The value used as the 'access_type' parameter (can be set to 'offline' to retrieve a refresh token)" />
<Setting PropertyName="Display" ParameterName="display" Type="String" Required="false"
Description="The value used as the 'display' parameter (can be set to 'touch' to adjust the authorization page display style for mobile apps)" />
</Provider>
<!--
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██ ██ ██ ██ ██ ▄▄▀██ ▄▄▄ ██ ▄▄ ██ ▄▄▄ █▄▄ ▄▄██

Loading…
Cancel
Save