Browse Source

Add PayPal support

pull/1538/head
Kévin Chalet 4 years ago
parent
commit
5b038a4831
  1. 50
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
  2. 56
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
  3. 13
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml

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

@ -20,7 +20,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers
*/
AmendIssuer.Descriptor,
AmendClientAuthenticationMethods.Descriptor,
AmendCodeChallengeMethods.Descriptor);
AmendCodeChallengeMethods.Descriptor,
AmendEndpoints.Descriptor);
/// <summary>
/// Contains the logic responsible for amending the issuer for the providers that require it.
@ -142,5 +143,52 @@ public static partial class OpenIddictClientWebIntegrationHandlers
return default;
}
}
/// <summary>
/// Contains the logic responsible for amending the endpoint URIs for the providers that require it.
/// </summary>
public class AmendEndpoints : IOpenIddictClientHandler<HandleConfigurationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<AmendEndpoints>()
.SetOrder(int.MaxValue - 100_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleConfigurationResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// While PayPal supports OpenID Connect discovery, the configuration document returned
// by the sandbox environment always contains the production endpoints, which would
// prevent the OpenIddict integration from working properly when using the sandbox mode.
// To work around that, the endpoints are manually overriden when this environment is used.
if (context.Registration.ProviderName is Providers.PayPal)
{
var options = context.Registration.GetPayPalOptions();
if (options.Environment is PayPal.Environments.Sandbox)
{
context.Configuration.AuthorizationEndpoint =
new Uri("https://www.sandbox.paypal.com/signin/authorize", UriKind.Absolute);
context.Configuration.JwksUri =
new Uri("https://api-m.sandbox.paypal.com/v1/oauth2/certs", UriKind.Absolute);
context.Configuration.TokenEndpoint =
new Uri("https://api-m.sandbox.paypal.com/v1/oauth2/token", UriKind.Absolute);
context.Configuration.UserinfoEndpoint =
new Uri("https://api-m.sandbox.paypal.com/v1/oauth2/token/userinfo", UriKind.Absolute);
}
}
return default;
}
}
}
}

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

@ -22,6 +22,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
HandleNonStandardFrontchannelErrorResponse.Descriptor,
AttachNonStandardClientAssertionTokenClaims.Descriptor,
AttachTokenRequestNonStandardClientCredentials.Descriptor,
OverrideValidatedBackchannelTokens.Descriptor,
AttachAdditionalUserinfoRequestParameters.Descriptor,
/*
@ -177,6 +178,47 @@ public static partial class OpenIddictClientWebIntegrationHandlers
}
}
/// <summary>
/// Contains the logic responsible for overriding the set
/// of required tokens for the providers that require it.
/// </summary>
public class OverrideValidatedBackchannelTokens : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<OverrideValidatedBackchannelTokens>()
.SetOrder(EvaluateValidatedBackchannelTokens.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
(context.ExtractBackchannelIdentityToken,
context.RequireBackchannelIdentityToken,
context.ValidateBackchannelIdentityToken) = context.Registration.ProviderName switch
{
// While PayPal claims the OpenID Connect flavor of the code flow is supported,
// their implementation doesn't return an id_token from the token endpoint.
Providers.PayPal => (false, false, false),
_ => (context.ExtractBackchannelIdentityToken,
context.RequireBackchannelIdentityToken,
context.ValidateBackchannelIdentityToken)
};
return default;
}
}
/// <summary>
/// Contains the logic responsible for attaching additional parameters
/// to the userinfo request for the providers that require it.
@ -204,16 +246,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
Debug.Assert(context.UserinfoRequest is not null, SR.GetResourceString(SR.ID4008));
// 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.
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), ")");
}
// StackOverflow requires sending an application key and a site parameter
// containing the name of the site from which the user profile is retrieved.
else if (context.Registration.ProviderName is Providers.StackExchange)
{
var options = context.Registration.GetStackExchangeOptions();
@ -222,13 +266,13 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.UserinfoRequest["site"] = options.Site;
}
// Twitter 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.
else if (context.Registration.ProviderName is Providers.Twitter)
{
var options = context.Registration.GetTwitterOptions();
// Twitter 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.
context.UserinfoRequest["expansions"] = string.Join(",", options.Expansions);
context.UserinfoRequest["tweet.fields"] = string.Join(",", options.TweetFields);
context.UserinfoRequest["user.fields"] = string.Join(",", options.UserFields);

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

@ -76,6 +76,19 @@
Description="The tenant used to identify the Azure AD instance (by default, the common tenant is used)" />
</Provider>
<Provider Name="PayPal" Documentation="https://developer.paypal.com/docs/log-in-with-paypal/">
<!--
Note: PayPal offers a production and a sandbox environment, but the sandbox server metadata
document doesn't reflect the configuration used by the sandbox environment (e.g the production
endpoints are always returned and the issuer is shared by both environments). To work around that,
the issuer configured globally is the same for both environments but the returned configuration
is amended by a dedicated handler to use the correct endpoints when the sandbox mode is used.
-->
<Environment Name="Production" Issuer="https://www.paypal.com/" />
<Environment Name="Sandbox" Issuer="https://www.paypal.com/" />
</Provider>
<Provider Name="Reddit" Documentation="https://github.com/reddit-archive/reddit/wiki/OAuth2">
<Environment Issuer="https://www.reddit.com/">
<Configuration AuthorizationEndpoint="https://www.reddit.com/api/v1/authorize"

Loading…
Cancel
Save