Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Zendesk #2217

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public ValueTask HandleAsync(PrepareUserInfoRequestContext context)
// Attach the authorization header containing the access token to the HTTP request.
request.Headers.Authorization = new AuthenticationHeaderValue(Schemes.Bearer, context.Request.AccessToken);

// Remove the access from the request payload to ensure it's not sent twice.
// Remove the access token from the request payload to ensure it's not sent twice.
context.Request.AccessToken = null;

return default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System.Collections.Immutable;
using System.Net.Http;
using System.Net.Http.Headers;
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlerFilters;
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
Expand All @@ -17,12 +18,102 @@ public static partial class OpenIddictClientWebIntegrationHandlers
public static class Revocation
{
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create([
/*
* Revocation request preparation:
*/
OverrideHttpMethod.Descriptor,
AttachBearerAccessToken.Descriptor,

/*
* Revocation response extraction:
*/
NormalizeContentType.Descriptor
]);

/// <summary>
/// Contains the logic responsible for overriding the HTTP method for the providers that require it.
/// </summary>
public sealed class OverrideHttpMethod : IOpenIddictClientHandler<PrepareRevocationRequestContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<PrepareRevocationRequestContext>()
.AddFilter<RequireHttpUri>()
.UseSingletonHandler<OverrideHttpMethod>()
.SetOrder(PreparePostHttpRequest<PrepareRevocationRequestContext>.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();

/// <inheritdoc/>
public ValueTask HandleAsync(PrepareRevocationRequestContext 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));

request.Method = context.Registration.ProviderType switch
{

ProviderTypes.Zendesk => HttpMethod.Delete,

_ => request.Method
};

return default;
}
}

/// <summary>
/// Contains the logic responsible for attaching the token to revoke
/// to the HTTP Authorization header for the providers that require it.
/// </summary>
public sealed class AttachBearerAccessToken : IOpenIddictClientHandler<PrepareRevocationRequestContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<PrepareRevocationRequestContext>()
.AddFilter<RequireHttpUri>()
.UseSingletonHandler<AttachBearerAccessToken>()
.SetOrder(AttachHttpParameters<PrepareRevocationRequestContext>.Descriptor.Order - 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();

/// <inheritdoc/>
public ValueTask HandleAsync(PrepareRevocationRequestContext 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));

// Zendesk requires using bearer authentication with the token that is going to be revoked.
if (context.Registration.ProviderType is ProviderTypes.Zendesk)
{
request.Headers.Authorization = new AuthenticationHeaderValue(Schemes.Bearer, context.Token);

// Remove the token from the request payload to ensure it's not sent twice.
context.Request.Token = null;
}

return default;
}
}

/// <summary>
/// Contains the logic responsible for normalizing the returned content
/// type of revocation responses for the providers that require it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,9 @@ public ValueTask HandleAsync(ExtractUserInfoResponseContext context)
ProviderTypes.ExactOnline => new(context.Response["d"]?["results"]?[0]?.GetNamedParameters() ??
throw new InvalidOperationException(SR.FormatID0334("d/results/0"))),

// Fitbit and Todoist return a nested "user" object.
ProviderTypes.Fitbit or ProviderTypes.Todoist => new(context.Response["user"]?.GetNamedParameters() ??
// Fitbit, Todoist, and Zendesk return a nested "user" object.
kevinchalet marked this conversation as resolved.
Show resolved Hide resolved
ProviderTypes.Fitbit or ProviderTypes.Todoist or ProviderTypes.Zendesk
=> new(context.Response["user"]?.GetNamedParameters() ??
throw new InvalidOperationException(SR.FormatID0334("user"))),

// Harvest returns a nested "user" object and a collection of "accounts".
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2153,6 +2153,35 @@
<Environment Issuer="https://api.login.yahoo.com/" />
</Provider>

<!--
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██ ▄▄▄ ██ ▄▄▄██ ▀██ ██ ▄▄▀██ ▄▄▄██ ▄▄▄ ██ █▀▄██
██▀▀▀▄▄██ ▄▄▄██ █ █ ██ ██ ██ ▄▄▄██▄▄▄▀▀██ ▄▀███
██ ▀▀▀ ██ ▀▀▀██ ██▄ ██ ▀▀ ██ ▀▀▀██ ▀▀▀ ██ ██ ██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-->

<Provider Name="Zendesk" Id="5577b117-1f3d-464f-b987-aabf7c0ab3f7"
Documentation="https://support.zendesk.com/hc/en-us/articles/4408845965210-Using-OAuth-authentication-with-your-application">
<!--
Note: Zendesk is a multitenant provider that relies on subdomains to identify instances.
As such, the following URLs all include a {settings.Tenant} placeholder that will be dynamically
replaced by OpenIddict at runtime by the tenant configured in the Zendesk settings.
-->

<Environment Issuer="https://{settings.Tenant}.zendesk.com/">
<Configuration AuthorizationEndpoint="https://{settings.Tenant}.zendesk.com/oauth/authorizations/new"
TokenEndpoint="https://{settings.Tenant}.zendesk.com/oauth/tokens"
UserInfoEndpoint="https://{settings.Tenant}.zendesk.com/api/v2/users/me"
RevocationEndpoint="https://{settings.Tenant}.zendesk.com/api/v2/oauth/tokens/current.json">
<CodeChallengeMethod Value="S256" />
</Configuration>
</Environment>

<Setting PropertyName="Tenant" ParameterName="tenant" Type="String" Required="true"
Description="Gets or sets the tenant used to identify the Zendesk instance" />
</Provider>

<!--
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██ ▄▄▄ ██ ▄▄▄ ██ ██ ██ ▄▄▄ ██
Expand Down