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

时间:2020-03-09 16:48:11

标签: c# integration-testing identityserver4

我正在尝试使集成测试针对受保护的api端点进行工作。 我的测试调用了IDS连接/令牌端点,并获取了有效令牌。当我使用它来调用受保护的api时,invalid_token总是会失败。 (该api也是IDS本地的)

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

我怀疑IDS验证代码的内部内容无法到达发现端点,因为它正在旋转自己的HttpClient。我知道WebApplicationFactory客户端是HttpClient的特殊实现。

有没有一种方法可以在配置/启动期间将WebApplicationFactory客户端注入IDS,以便它可以正常工作?

还是有一种方法可以使伪造的授权端点仅验证Auth标头中发送的任何令牌?

我希望我的集成测试能够针对api进行工作,如果它实际上验证了令牌,那真是太好了,但如果我不能伪造它,那就太好了。

谢谢。

2 个答案:

答案 0 :(得分:1)

以惯常的方式,在我问了一些问题之后,我立即弄清楚了。这是基于另一个问题的简单解决方案,其答案有些复杂(How can I set my IdentityServer4 BackChannelHandler from within an xUnit integration test using WebApplicationFactory?)。

在您的Startup.cs中添加一个静态属性:

string query = "INSERT INTO flexTable SELECT MapJSONExtractor(@flexDataToInsert) as event"
var connection = GetConnection(); // returns OdbcConnection
var command = GetCommand(connection, queryString.ToString()); // returns OdbcCommand
OdbcParameter param = command.Parameters.Add("@flexDataToInsert", OdbcType.NText, 2048);
param.Value = data; // long json to insert
await command.ExecuteNonQueryAsync()

然后将其添加到您的/// <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>>

这将允许您调用令牌端点并接收真实令牌,并将其用作集成测试中的身份验证标头。热得发呆。

答案 1 :(得分:0)

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

创建一个如下所示的类:

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);
    }
}

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

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

builder.ConfigureServices(services => { 部分。

然后在你的测试类中添加

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

当您想要模拟用户时,将其添加到您的 ctor 或测试方法,以便在您的控制器尝试使用 Authorize 执行某些操作时 User 属性起作用。 (Client 是我为 Factory.CreateClient(); 提供的便利属性)

这样做的好处是,如果您不希望通过授权用户,请不要设置 my-id 和 my-name 标头。

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