From 7bea78e387f0f66d81f5e5ab6344af384fed5f47 Mon Sep 17 00:00:00 2001 From: Elizabeth Schneider Date: Wed, 20 Nov 2024 00:21:01 -0700 Subject: [PATCH] cleanupcleanup and added revocation --- .../Program.cs | 45 ++++++---- ...ClientWebIntegrationHandlers.Revocation.cs | 89 +++++++++++++++++++ .../OpenIddictClientWebIntegrationHandlers.cs | 9 ++ ...penIddictClientWebIntegrationProviders.xml | 12 +-- 4 files changed, 128 insertions(+), 27 deletions(-) diff --git a/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs b/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs index 12dab54ed..6382ea206 100644 --- a/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs +++ b/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs @@ -69,19 +69,19 @@ .SetProductInformation(typeof(Program).Assembly); // Add a client registration matching the client application definition in the server project. - options.AddRegistration(new OpenIddictClientRegistration - { - Issuer = new Uri("https://localhost:44395/", UriKind.Absolute), - ProviderName = "Local", - ProviderDisplayName = "Local authorization server", + //options.AddRegistration(new OpenIddictClientRegistration + //{ + // Issuer = new Uri("https://localhost:44395/", UriKind.Absolute), + // ProviderName = "Local", + // ProviderDisplayName = "Local authorization server", - ClientId = "console", + // ClientId = "console", - PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative), - RedirectUri = new Uri("callback/login/local", UriKind.Relative), + // PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative), + // RedirectUri = new Uri("callback/login/local", UriKind.Relative), - Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" } - }); + // Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" } + //}); // Register the Web providers integrations. // @@ -90,17 +90,24 @@ // parameter containing their URL as part of authorization responses. For more information, // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. options.UseWebProviders() - .AddGitHub(options => + .AddZendesk(options => { - options.SetClientId("992372d088f8676a7945") - .SetClientSecret("1f18c22f766e44d7bd4ea4a6510b9e337d48ab38") - .SetRedirectUri("callback/login/github"); - }) - .AddTwitter(options => - { - options.SetClientId("bXgwc0U3N3A3YWNuaWVsdlRmRWE6MTpjaQ") - .SetRedirectUri("callback/login/twitter"); + options.SetTenant("csharpapi") + .SetClientId("openiddict_testing") + .SetRedirectUri("callback/login/zendesk") + .AddScopes(["read", "write"]); }); + //.AddGitHub(options => + //{ + // options.SetClientId("992372d088f8676a7945") + // .SetClientSecret("1f18c22f766e44d7bd4ea4a6510b9e337d48ab38") + // .SetRedirectUri("callback/login/github"); + //}) + //.AddTwitter(options => + //{ + // options.SetClientId("bXgwc0U3N3A3YWNuaWVsdlRmRWE6MTpjaQ") + // .SetRedirectUri("callback/login/twitter"); + //}); }); // Register the worker responsible for creating the database used to store tokens diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Revocation.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Revocation.cs index d3c614fb6..a5646fcc1 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Revocation.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Revocation.cs @@ -6,8 +6,10 @@ 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.SystemNetHttp.OpenIddictClientSystemNetHttpHandlers.UserInfo; using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; namespace OpenIddict.Client.WebIntegration; @@ -17,12 +19,99 @@ public static partial class OpenIddictClientWebIntegrationHandlers public static class Revocation { public static ImmutableArray DefaultHandlers { get; } = ImmutableArray.Create([ + + /* + * Revocation request preparation: + */ + OverrideHttpMethod.Descriptor, + AttachAccessTokenParameter.Descriptor, /* * Revocation response extraction: */ NormalizeContentType.Descriptor ]); + /// + /// Contains the logic responsible for overriding the HTTP method for the providers that require it. + /// + public sealed class OverrideHttpMethod : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler() + .SetOrder(PreparePostHttpRequest.Descriptor.Order + 250) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + 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; + } + } + + /// + /// Contains the logic responsible for attaching the access token + /// parameter to the request for the providers that require it. + /// + public sealed class AttachAccessTokenParameter : IOpenIddictClientHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler() + .SetOrder(AttachBearerAccessToken.Descriptor.Order + 250) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + 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 the token that is going to be revoked + if (context.Registration.ProviderType is ProviderTypes.Zendesk) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.Token); + } + + return default; + } + } + /// /// Contains the logic responsible for normalizing the returned content /// type of revocation responses for the providers that require it. diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs index a2a4f32b9..4f858e128 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs @@ -1362,6 +1362,9 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) // Shopify returns the email address as a custom "associated_user/email" node in token responses: ProviderTypes.Shopify => (string?) context.TokenResponse?["associated_user"]?["email"], + + // Zendesk returns the emails address as a custom "user/email" node: + ProviderTypes.Zendesk => (string?) context.UserInfoResponse?["user"]?["email"], _ => context.MergedPrincipal.GetClaim(ClaimTypes.Email) }); @@ -1441,6 +1444,9 @@ ProviderTypes.Spotify or ProviderTypes.StackExchange or ProviderTypes.Zoom // Typeform returns the username as a custom "alias" node: ProviderTypes.Typeform => (string?) context.UserInfoResponse?["alias"], + // Zendesk returns the username as a custom "user/name" node: + ProviderTypes.Zendesk => (string?) context.UserInfoResponse?["user"]?["name"], + // Zoho returns the username as a custom "Display_Name" node: ProviderTypes.Zoho => (string?) context.UserInfoResponse?["Display_Name"], @@ -1529,6 +1535,9 @@ ProviderTypes.Twitter or ProviderTypes.Weibo or ProviderTypes.Zoom // WordPress returns the user identifier as a custom "ID" node: ProviderTypes.WordPress => (string?) context.UserInfoResponse?["ID"], + // Zendesk returns the user identifier as a custom "user/id" node: + ProviderTypes.Zendesk => (string?) context.UserInfoResponse?["user"]?["id"], + // WordPress returns the user identifier as a custom "ZUID" node: ProviderTypes.Zoho => (string?) context.UserInfoResponse?["ZUID"], diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml index fcf83109a..3e521d117 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml @@ -2153,7 +2153,7 @@ - +