Browse Source

Derive the OpenIddict opt-in URLs from the configured server endpoints

pull/26017/head
maliming 12 hours ago
parent
commit
d79fa71b41
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 14
      framework/test/Volo.Abp.AspNetCore.Uow.Tests/Volo/Abp/AspNetCore/Uow/UnitOfWorkMiddleware_Relational_Tests.cs
  2. 36
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs
  3. 13
      modules/openiddict/test/Volo.Abp.OpenIddict.AspNetCore.Tests/Volo/Abp/OpenIddict/Integration/OpenIddictTokenEndpoint_Integration_Tests.cs
  4. 7
      modules/openiddict/test/Volo.Abp.OpenIddict.AspNetCore.Tests/Volo/Abp/OpenIddict/Integration/OpenIddictTokenIntegrationTestModule.cs

14
framework/test/Volo.Abp.AspNetCore.Uow.Tests/Volo/Abp/AspNetCore/Uow/UnitOfWorkMiddleware_Relational_Tests.cs

@ -124,18 +124,22 @@ public class UnitOfWorkMiddleware_Relational_Tests : AbpWebApplicationFactoryInt
EnableCompleteOnResponseStarting();
var name = Guid.NewGuid().ToString("N");
HttpResponseMessage response = null;
Exception surfaced = null;
try
{
var response = await Client.GetAsync("/api/uow-visibility/insert-then-throw-in-serialization?name=" + name);
response = await Client.GetAsync("/api/uow-visibility/insert-then-throw-in-serialization?name=" + name);
await response.Content.ReadAsStringAsync();
}
catch (Exception)
catch (Exception ex)
{
surfaced = ex;
}
// The action saved the row, then serializing the result failed before the response started. The error
// response is written by the upstream exception middleware after the request unit of work is disposed,
// so response-start completion must not commit the failed request.
// The action ran and saved the row, then serializing the result failed. The request must therefore
// fail with a server error (not a 404 or a success), and the error response, written by the upstream
// exception middleware after the request unit of work is disposed, must not commit the failed request.
(surfaced != null || (response != null && (int)response.StatusCode >= 500)).ShouldBeTrue();
(await CountAsync(name)).ShouldBe(0);
}
}

36
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs

@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Server;
using Volo.Abp.AspNetCore.MultiTenancy;
@ -48,12 +50,36 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule
options.RemoveClientIdClaim();
});
// Complete tokens/authorizations/sessions written while processing OpenIddict requests before the response starts.
Configure<AbpAspNetCoreUnitOfWorkOptions>(options =>
// Complete data written while processing OpenIddict requests before the response starts.
// Derived from the configured server endpoint paths so remapped endpoints are covered too.
context.Services.AddOptions<AbpAspNetCoreUnitOfWorkOptions>()
.Configure<IOptions<OpenIddictServerOptions>>((uowOptions, serverOptions) =>
{
foreach (var path in GetServerEndpointPaths(serverOptions.Value))
{
uowOptions.CompleteUnitOfWorkOnResponseStartingUrls.AddIfNotContains(path);
}
});
}
private static IEnumerable<string> GetServerEndpointPaths(OpenIddictServerOptions serverOptions)
{
var endpoints = serverOptions.TokenEndpointUris
.Concat(serverOptions.AuthorizationEndpointUris)
.Concat(serverOptions.DeviceAuthorizationEndpointUris)
.Concat(serverOptions.PushedAuthorizationEndpointUris)
.Concat(serverOptions.EndSessionEndpointUris)
.Concat(serverOptions.RevocationEndpointUris)
.Concat(serverOptions.EndUserVerificationEndpointUris);
foreach (var uri in endpoints)
{
options.CompleteUnitOfWorkOnResponseStartingUrls.AddIfNotContains("/connect");
options.CompleteUnitOfWorkOnResponseStartingUrls.AddIfNotContains("/device");
});
var path = uri.IsAbsoluteUri ? uri.AbsolutePath : "/" + uri.OriginalString.TrimStart('/');
if (path.Length > 1)
{
yield return path;
}
}
}
private void AddOpenIddictServer(IServiceCollection services)

13
modules/openiddict/test/Volo.Abp.OpenIddict.AspNetCore.Tests/Volo/Abp/OpenIddict/Integration/OpenIddictTokenEndpoint_Integration_Tests.cs

@ -34,7 +34,7 @@ public class OpenIddictTokenEndpoint_Integration_Tests : AbpWebApplicationFactor
[Fact]
public async Task Token_Row_Is_Committed_Before_The_Connect_Token_Response_Is_Sent()
{
// The OpenIddict module opts "/connect" in by default.
// The OpenIddict module opts its configured endpoint paths (including "/connect/token") in by default.
var response = await RequestTokenAsync();
response.StatusCode.ShouldBe(HttpStatusCode.OK);
@ -55,4 +55,15 @@ public class OpenIddictTokenEndpoint_Integration_Tests : AbpWebApplicationFactor
response.StatusCode.ShouldBe(HttpStatusCode.OK);
TokenCountAtResponseStart.ShouldBe(0);
}
[Fact]
public void The_Configured_OpenIddict_Endpoint_Paths_Are_Opted_In()
{
// The opt-in list is derived from the configured server endpoints, so a non-"/connect" endpoint
// like "/device" is covered, and the custom "/my-custom/token" endpoint the test host registered
// is followed too - a hardcoded "/connect" prefix would miss both.
Options.CompleteUnitOfWorkOnResponseStartingUrls.ShouldContain("/connect/token");
Options.CompleteUnitOfWorkOnResponseStartingUrls.ShouldContain("/device");
Options.CompleteUnitOfWorkOnResponseStartingUrls.ShouldContain("/my-custom/token");
}
}

7
modules/openiddict/test/Volo.Abp.OpenIddict.AspNetCore.Tests/Volo/Abp/OpenIddict/Integration/OpenIddictTokenIntegrationTestModule.cs

@ -60,6 +60,13 @@ public class OpenIddictTokenIntegrationTestModule : AbpModule
{
context.Services.AddSingleton<TokenVisibilityRecorder>();
// A remapped token endpoint, so the tests can prove the opt-in list is derived from the configured
// server endpoints (custom endpoints are followed) rather than a hardcoded "/connect" prefix.
Configure<OpenIddictServerOptions>(options =>
{
options.TokenEndpointUris.Add(new Uri("my-custom/token", UriKind.Relative));
});
// The OpenIddict controllers (including the token endpoint) live in a referenced assembly.
context.Services.GetSingletonInstance<ApplicationPartManager>()
.ApplicationParts.AddIfNotContains(typeof(AbpOpenIddictAspNetCoreModule).Assembly);

Loading…
Cancel
Save