将 IdentityServer4 与 WebApplicationFactory 集成测试结合使用

Using IdentityServer4 with WebApplicationFactory Integration Tests

我正在尝试让我的集成测试针对受保护的 api 端点进行工作。 我的测试调用 IDS connect/token 端点并获得有效令牌。当我用它来调用受保护的 api 时,我总是会遇到 invalid_token 的失败。 (api 对于 IDS 也是本地的)

如果我手动调试 IDS 并使用 postman 获取令牌,然后手动设置它并使用 postman 调用受保护的 api 它工作正常。

我怀疑 IDS 验证代码的内部无法访问发现端点,因为它正在启动自己的 HttpClient。我知道WebApplicationFactory Client是HttpClient的一个特殊实现。

有没有一种方法可以在 configuration/startup 期间将 WebApplicationFactory 客户端注入 IDS 以使其正常工作?

或者有没有一种方法可以制作一个伪造的授权端点,它只验证 Auth header 中发送的任何令牌?

我只是希望我的集成测试能够针对 api,如果它真的验证了令牌就太好了,但如果不能,我可以伪造它。

谢谢。

以通常的方式,在我问了一些我被难住了几个小时的事情之后,我就想通了。这是基于另一个问题的简单解决方案,它的答案有点复杂 (How can I set my IdentityServer4 BackChannelHandler from within an xUnit integration test using WebApplicationFactory?)。

在你的 Startup.cs 中添加静态 属性:

/// <summary>
/// For integrationtesting set this to Factory.Server.CreateHandler()
/// </summary>
public static HttpMessageHandler JwtBackChannelHandler { get; set; }

然后将此添加到您的 .AddIdentityServerAuthentication() 选项中:

if (JwtBackChannelHandler != null)
{
    options.JwtBackChannelHandler = JwtBackChannelHandler;
}

在实现 IClassFixture<WebApplicationFactory<Startup>> 的测试 class 的构造函数中添加:

Startup.JwtBackChannelHandler = Factory.Server.CreateHandler();

这将允许您调用令牌端点并接收真实令牌,并在您的集成测试中将其用作身份验证 header。火爆的数字。

这个答案是针对@liqSTAR 的,关于如何在集成测试中忽略 IdentitiyServer 4 和伪造授权。

创建如下所示的 class:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public TestAuthHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder, 
        ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var context = Context;
        List<Claim> claims;
        if (context.Request.Headers.Keys.Contains("my-name"))
        {
            var name = context.Request.Headers["my-name"].First();
            var id = context.Request.Headers.Keys.Contains("my-id") ? context.Request.Headers["my-id"].First() : "";
            claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, name),
                    new Claim(ClaimTypes.NameIdentifier, id),
                };
        }
        else
        {
            claims = new List<Claim> { new Claim(ClaimTypes.Name, "Test user") };
        }
        var identity = new ClaimsIdentity(claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        context.User = principal;

        var ticket = new AuthenticationTicket(principal, "Test");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

然后在继承自 WebApplicationFactory<TStartup> 的自定义 class 中添加 override void ConfigureWebHost(IWebHostBuilder builder) 添加

services.AddAuthentication("Test")
  .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => {});

转到 builder.ConfigureServices(services => { 部分。

然后在你的测试中class添加

Client.DefaultRequestHeaders.Add("my-id", "1234");
Client.DefaultRequestHeaders.Add("my-name", "Test_User");

当你想模拟一个用户时,你的 ctor 或测试方法,这样 Authorize 属性在你的控制器试图用 User 做某事时起作用。 (Client 方便 属性 我有 Factory.CreateClient();

这样做的好处是,如果您不想通过授权用户,请不要设置 my-id 和 my-name headers.

我希望这能帮助您朝着正确的方向前进。