From 0ba49f51cf8e69530b42c52f8411475e5360f6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Tue, 7 Mar 2023 16:53:00 +0100 Subject: [PATCH] Add Trovo to the list of supported providers --- ...ctClientWebIntegrationHandlers.Exchange.cs | 96 +++++++++++++++++++ ...ctClientWebIntegrationHandlers.Userinfo.cs | 18 +++- .../OpenIddictClientWebIntegrationHandlers.cs | 45 +++++++++ ...penIddictClientWebIntegrationProviders.xml | 31 ++++++ 4 files changed, 186 insertions(+), 4 deletions(-) diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs index 2b1f3003..d85a3464 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Exchange.cs @@ -7,8 +7,10 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Net.Http.Headers; +using System.Net.Http.Json; using System.Text; using OpenIddict.Extensions; +using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpConstants; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlerFilters; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers; using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers.Exchange; @@ -25,7 +27,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers * Token request preparation: */ AttachNonStandardBasicAuthenticationCredentials.Descriptor, + AttachNonStandardRequestHeaders.Descriptor, AttachNonStandardQueryStringParameters.Descriptor, + AttachNonStandardRequestPayload.Descriptor, /* * Token response extraction: @@ -99,6 +103,50 @@ public static partial class OpenIddictClientWebIntegrationHandlers } } + /// + /// Contains the logic responsible for attaching additional + /// headers to the request for the providers that require it. + /// + public sealed class AttachNonStandardRequestHeaders : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler() + .SetOrder(AttachUserAgentHeader.Descriptor.Order + 250) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(PrepareTokenRequestContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + // Trovo requires sending the client identifier in a non-standard "client-id" header and + // the client secret in the payload (formatted using JSON instead of the standard format). + if (context.Registration.ProviderName is Providers.Trovo) + { + request.Headers.Add("Client-ID", context.Request.ClientId); + + // Remove the client identifier from the request payload to ensure it's not sent twice. + context.Request.ClientId = null; + } + + return default; + } + } + /// /// Contains the logic responsible for attaching non-standard query string /// parameters to the token request for the providers that require it. @@ -147,6 +195,54 @@ public static partial class OpenIddictClientWebIntegrationHandlers } } + /// + /// Contains the logic responsible for attaching a non-standard payload for the providers that require it. + /// + public sealed class AttachNonStandardRequestPayload : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler() + .SetOrder(AttachFormParameters.Descriptor.Order + 500) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(PrepareTokenRequestContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008)); + + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + request.Content = context.Registration.ProviderName switch + { + // Trovo returns a 500 internal server error when using the standard + // "application/x-www-form-urlencoded" format and requires using JSON. + Providers.Trovo => JsonContent.Create(context.Transaction.Request, + new MediaTypeHeaderValue(MediaTypes.Json) + { + CharSet = Charsets.Utf8 + }), + + _ => request.Content + }; + + return default; + } + } + /// /// Contains the logic responsible for attaching non-standard query string /// parameters to the token request for the providers that require it. diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs index 10e30d85..e7ef263c 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Userinfo.cs @@ -67,12 +67,16 @@ public static partial class OpenIddictClientWebIntegrationHandlers // (which is statically set to the last version known to be supported by the OpenIddict integration). if (context.Registration.ProviderName is Providers.Trakt) { - var options = context.Registration.GetTraktOptions(); - - request.Headers.Add("trakt-api-key", options.ClientId); + request.Headers.Add("trakt-api-key", context.Registration.ClientId); request.Headers.Add("trakt-api-version", "2"); } + // Trovo requires sending the client identifier as a separate, non-standard header. + else if (context.Registration.ProviderName is Providers.Trovo) + { + request.Headers.Add("Client-ID", context.Registration.ClientId); + } + return default; } } @@ -109,15 +113,21 @@ public static partial class OpenIddictClientWebIntegrationHandlers // By default, OpenIddict sends the access token as part of the Authorization header // using the Bearer authentication scheme. Some providers don't support this method - // and require sending the access token as part of the userinfo request payload. + // and require sending the access token as part of the userinfo request payload + // or using a non-standard authentication scheme (e.g OAuth instead of Bearer). (context.Request.AccessToken, request.Headers.Authorization) = context.Registration.ProviderName switch { + // These providers require sending the access token as part of the request payload. Providers.Deezer or Providers.Mixcloud or Providers.StackExchange => (request.Headers.Authorization?.Parameter, null), + // Trovo requires using the "OAuth" scheme instead of the standard "Bearer" value. + Providers.Trovo + => (null, new AuthenticationHeaderValue("OAuth", request.Headers.Authorization?.Parameter)), + _ => (context.Request.AccessToken, request.Headers.Authorization) }; diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs index 7b47aa51..451b90ae 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs @@ -22,6 +22,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers * Authentication processing: */ HandleNonStandardFrontchannelErrorResponse.Descriptor, + OverrideTokenEndpoint.Descriptor, AttachNonStandardClientAssertionTokenClaims.Descriptor, AttachTokenRequestNonStandardClientCredentials.Descriptor, AdjustRedirectUriInTokenRequest.Descriptor, @@ -125,6 +126,46 @@ public static partial class OpenIddictClientWebIntegrationHandlers } } + /// + /// Contains the logic responsible for overriding the address + /// of the token endpoint for the providers that require it. + /// + public sealed class OverrideTokenEndpoint : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(ResolveTokenEndpoint.Descriptor.Order + 500) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ProcessAuthenticationContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.TokenEndpoint = context.Registration.ProviderName switch + { + // Trovo uses a different token endpoint for the refresh token grant. + // + // For more information, see + // https://developer.trovo.live/docs/APIs.html#_4-3-refresh-access-token. + Providers.Trovo when context.GrantType is GrantTypes.RefreshToken + => new Uri("https://open-api.trovo.live/openplatform/refreshtoken", UriKind.Absolute), + + _ => context.TokenEndpoint + }; + + return default; + } + } + /// /// Contains the logic responsible for amending the client /// assertion methods for the providers that require it. @@ -639,6 +680,10 @@ public static partial class OpenIddictClientWebIntegrationHandlers // the standard format (that requires using a space as the scope separator): Providers.Deezer => string.Join(",", context.Scopes), + // The following providers are known to use plus-separated scopes instead of + // the standard format (that requires using a space as the scope separator): + Providers.Trovo => string.Join("+", context.Scopes), + _ => context.Request.Scope }; diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index 05ed1439..daa7e5d7 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -632,6 +632,37 @@ + + + + + + + + + + + + + + + + +