Как я могу установить свой IdentityServer4 BackChannelHandler из теста интеграции xUnit с помощью WebApplicationFactory? - PullRequest
0 голосов
/ 23 октября 2018

Обновление: после исправления проблемы с сертификатом я теперь получаю 500 ответов от теста с этим сообщением:

InvalidOperationException: IDX20803: Невозможно получить конфигурацию из: 'http://localhost/.well-known/openid-configuration'.

Это похоже на эту проблему: https://github.com/IdentityServer/IdentityServer4/issues/685; однако я не могу придумать способ установить клиент или обработчик обратного канала из моего теста - похоже,ситуация с курицей и яйцом.


Эта проблема была исправлена ​​с помощью реального файла сертификата / .pfx.Это привело к вышеуказанной проблеме.

Я использую WebApplicationFactory для проведения интеграционных тестов через мой API, и я думаю, что я рассмотрел все основы для правильной настройки http-клиентов.Я получаю сообщение об ошибке при вызове действия в моем API.

Это ошибка при выполнении get для API с токеном:

WWW-Authenticate: ошибка канала= "invalid_token", error_description = "Ключ подписи не найден"

Вот простой тестовый класс, который демонстрирует эту проблему:

public class EntityControllerShould : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public EntityControllerShould(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task ReturnListOfEntities_ForGet()
    {
        // arrange
        _factory.CreateClient();

        var handler = _factory.Server.CreateHandler();

        var client = new HttpClient(handler) { BaseAddress = new System.Uri("http://localhost/") };

        // discover endpoints from metadata
        var dc = new DiscoveryClient(client.BaseAddress.ToString(), handler);
        var disco = await dc.GetAsync();
        if (disco.IsError)
        {
            Assert.True(false);
        }
        // request token
        var tokenClient = new TokenClient(disco.TokenEndpoint, "api_client", "secret", handler);
        var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

        if (tokenResponse.IsError)
        {
            Assert.True(false);
        }

        client.SetBearerToken(tokenResponse.AccessToken);

        // act
        var response = await client.GetAsync("api/entity/?id=123");
        // response code is 401 with the above quoted error in the header
        response.EnsureSuccessStatusCode();

        var responseString = await response.Content.ReadAsStringAsync();

        // assert
        Assert.NotNull(responseString);
    }
}

Snip из Startup.cs вПроект API:

services.AddIdentityServer()
            .AddSigningCredential(myCertificate)
            .AddSigningCredential()
            .AddClientStore<CustomClientStore>()
            .AddInMemoryIdentityResources(IdentityServerConfig.IdentityResources)
            .AddInMemoryApiResources(IdentityServerConfig.Apis);
  • Я размещаю IdentityServer4 и API в одном проекте.
  • Когда я вручную выполняю интеграционные тесты через браузер, он работает просто отлично
  • Я обнаружил это выглядит очень похоже

Есть ли что-то, что мне нужно учитывать при запуске в контексте теста xunit, которым я не являюсь?

1 Ответ

0 голосов
/ 27 декабря 2018

Единственное решение, которое я придумал, - это установить статический обработчик в классе Startup проекта API, а затем переопределить его в каждом модульном тесте.

В частности:

// startup.cs
public static HttpMessageHandler Handler { get; set; }

// snip from configureservices
.AddJwtBearer(jwt =>
 {
      // defaults as they were
      jwt.Authority = "http://localhost:5000/";
      jwt.RequireHttpsMetadata = false;
      jwt.Audience = "api1";
      // if static handler is null don't change anything, otherwise assume integration test.
      if(Handler == null)
      {
          jwt.BackchannelHttpHandler = Handler;
          jwt.Authority = "http://localhost/";
      }
  });

Полный список из проекта IdSvrAndApi:

 public class Startup
 {
     public Startup(IConfiguration configuration)
     {
         Configuration = configuration;
     }

     public IConfiguration Configuration { get; }
     public static HttpMessageHandler Handler { get; set; }

     public void ConfigureServices(IServiceCollection services)
     {
         services.AddMvc();

         services.AddIdentityServer()
             .AddDeveloperSigningCredential()
             .AddInMemoryIdentityResources(Config.IdentityResources)
             .AddInMemoryClients(Config.Clients)
             .AddInMemoryApiResources(Config.Apis)
             .AddTestUsers(TestUsers.Users);

        services.AddAuthentication()
           .AddJwtBearer(jwt =>
           {
               jwt.BackchannelHttpHandler = Handler;
               jwt.Authority = "http://localhost/";
               jwt.RequireHttpsMetadata = false;
               jwt.Audience = "api1";
           });
            .AddJwtBearer(jwt =>
            {
                // defaults as they were
                jwt.Authority = "http://localhost:5000/";
                jwt.RequireHttpsMetadata = false;
                jwt.Audience = "api1";
                // if static handler is null don't change anything, otherwise assume integration test.
                if(Handler == null)
                {
                    jwt.BackchannelHttpHandler = Handler;
                    jwt.Authority = "http://localhost/";
                }
            });
    }

Полностью рабочий образец этого можно увидеть здесь: https://github.com/fuzzzerd/IdentityServerAndApi.

Я не получил однозначного ответа, если это рекомендуемый способ добиться этого, но я открыл вопрос по проекту здесь: https://github.com/IdentityServer/IdentityServer4/issues/2877

...