Тестирование интеграции XUnit с Identityserver: токен, полученный от Identityserver, не может соответствовать ключу
В настоящее время я пытаюсь реализовать тестирование интеграции в среде с двумя серверами:
- Сервер API.NET Core
- Сервер аутентификации IdentityServer4
Благодаря долгой борьбе мне удалось заставить этих двоих общаться друг с другом, однако IdentityServer выдает следующую ошибку при попытке подтвердить токен JWT (через API):
Носитель не был аутентифицирован. Сообщение об ошибке: IDX10501: Ошибка проверки подписи. Невозможно сопоставить ключ: kid: '8C1D5950D083E20D4B20DE9B37AC71FAEF679469'.
Я постараюсь сделать пример кода кратким:
При запуске XUnit я настраиваю и создаю клиентов для обоих TestServer.
public APITestBase(ITestOutputHelper output)
{
_output = output;
var apicon = new ConfigurationBuilder()
.AddJsonFile("apisettings.json", optional: true, reloadOnChange: true)
.Build();
var authcon = new ConfigurationBuilder()
.AddJsonFile("authsettings.json", optional: true, reloadOnChange: true)
.Build();
_authServer = new TestServer(new WebHostBuilder()
.UseConfiguration(authcon)
.ConfigureLogging(x => x.AddXUnit(_output))
.UseEnvironment("Development")
.UseStartup<Auth.Startup>());
_authClient = _authServer.CreateClient();
_server = new TestServer(new WebHostBuilder()
.UseConfiguration(apicon)
.ConfigureLogging(x => x.AddXUnit(_output))
.UseEnvironment("Test")
.ConfigureServices(
services => {
//Here, I'm adding an httpclienthandler. In the application I will use this as JWTBackChannelHandler. This allows communication between the two servers
services.AddSingleton<HttpMessageHandler>(_authServer.CreateHandler());
})
.UseStartup<Api.Startup>());
_client = _server.CreateClient();
APIConfig = new Cnfg();
apicon.Bind("APIConfig", APIConfig);
//Make users in api
}
Вот настоящий тест, который я пытаюсь запустить
[Fact]
public async Task ApplyTest()
{
_client.SetBearerToken(await GetAccessToken());
// Act
var response = await _client.GetAsync($"{APIConfig.ApiBaseUrl}api/call/");
response.EnsureSuccessStatusCode();
}
А вот GetAccessToken, который возвращает токен JWT, который интеграция использует для идентификации себя как пользователя... По крайней мере, таков план.
protected async Task<string> GetAccessToken()
{
var disco = await _authClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
{
Address = _authServer.BaseAddress.ToString(),
Policy =
{
ValidateIssuerName = false
}
});
if (!String.IsNullOrEmpty(disco.Error))
{
throw new Exception(disco.Error);
}
var response = await _authClient.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
GrantType = "password",
ClientId = "api",
UserName = "test@test.nl",
Password = "test"
});
return response.AccessToken;
}
Единственная различимая разница, которую я могу найти между токеном JWT, сгенерированным с помощью этой функции, и токеном обычного запроса веб-сайта, заключается в том, что открытый ключ отсутствует (согласно jwt.io).
Наконец, поскольку я думаю, что этот бит может быть полезен, вот часть API Startup.cs, где я принудительно использую JWTBackChannelHandler
if (Environment.EnvironmentName == "Test")
{
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, o =>
{
o.JwtBackChannelHandler = _identityServerMessageHandler;
o.RequireHttpsMetadata = false;
})
.AddApiKeyAuthenication(o =>
{
o.Keys.Add("****".ToSha256());
o.Keys.Add("****".ToSha256());
});
}
Заранее большое спасибо за вашу помощь, я вроде как дошел до предела с этим. Пожалуйста, дайте мне знать, если вам нужна дополнительная информация, и я буду рад ее предоставить.
1 ответ
Еще через 2 часа копания обнаружил, что нужно добавить
o.Authority = identityServerOptions.Address.AbsoluteUri;
К файлу AddIdentityServerAuthentication.
Спасибо тем, кто нашел время разобраться в моей проблеме для меня.