diff --git a/src/Umbraco.Cms.Api.Common/DependencyInjection/ProcessRequestContextHandler.cs b/src/Umbraco.Cms.Api.Common/DependencyInjection/ProcessRequestContextHandler.cs new file mode 100644 index 0000000000..c680ab6b34 --- /dev/null +++ b/src/Umbraco.Cms.Api.Common/DependencyInjection/ProcessRequestContextHandler.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Http; +using OpenIddict.Server; +using OpenIddict.Validation; +using Umbraco.Cms.Core; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Common.DependencyInjection; + +public class ProcessRequestContextHandler + : IOpenIddictServerHandler, IOpenIddictValidationHandler +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly string _backOfficePathSegment; + + public ProcessRequestContextHandler(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + _backOfficePathSegment = Constants.System.DefaultUmbracoPath.TrimStart(Constants.CharArrays.Tilde) + .EnsureStartsWith('/') + .EnsureEndsWith('/'); + } + + public ValueTask HandleAsync(OpenIddictServerEvents.ProcessRequestContext context) + { + if (SkipOpenIddictHandlingForRequest()) + { + context.SkipRequest(); + } + + return ValueTask.CompletedTask; + } + + public ValueTask HandleAsync(OpenIddictValidationEvents.ProcessRequestContext context) + { + if (SkipOpenIddictHandlingForRequest()) + { + context.SkipRequest(); + } + + return ValueTask.CompletedTask; + } + + private bool SkipOpenIddictHandlingForRequest() + { + var requestPath = _httpContextAccessor.HttpContext?.Request.Path.Value; + if (requestPath.IsNullOrWhiteSpace()) + { + return false; + } + + return requestPath.StartsWith(_backOfficePathSegment) is false; + } +} diff --git a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs index b61f842b5a..7e730695f3 100644 --- a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs +++ b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs @@ -2,6 +2,8 @@ using System.Security.Cryptography; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using OpenIddict.Server; +using OpenIddict.Validation; using Umbraco.Cms.Api.Common.Security; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; @@ -96,6 +98,13 @@ public static class UmbracoBuilderAuthExtensions options .AddEncryptionKey(new SymmetricSecurityKey(RandomNumberGenerator.GetBytes(32))) // generate a cryptographically secure random 256-bits key .AddSigningKey(new RsaSecurityKey(RSA.Create(keySizeInBits: 2048))); // generate RSA key with recommended size of 2048-bits + + // Add custom handler for the "ProcessRequestContext" server event, to stop OpenIddict from handling + // every last request to the server (including front-end requests). + options.AddEventHandler(configuration => + { + configuration.UseSingletonHandler().SetOrder(OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1); + }); }) // Register the OpenIddict validation components. @@ -113,6 +122,13 @@ public static class UmbracoBuilderAuthExtensions // Use ASP.NET Core Data Protection for tokens instead of JWT. (see note in AddServer) options.UseDataProtection(); + + // Add custom handler for the "ProcessRequestContext" validation event, to stop OpenIddict from handling + // every last request to the server (including front-end requests). + options.AddEventHandler(configuration => + { + configuration.UseSingletonHandler().SetOrder(OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlers.ResolveRequestUri.Descriptor.Order - 1); + }); }); builder.Services.AddRecurringBackgroundJob();