Вопрос о поддержке ASP.NET Core 3 Identity / Identity Server / SPA для типа предоставления пароля владельца ресурса

Согласно аутентификации и авторизация для SPA, я создал новый SPA с поддержкой авторизации API. Вы можете просмотреть это на GitHub.

Для поддержки интеграционных тестов я добавил новый клиент (см. настройки приложения .json), которому разрешен тип предоставления пароля владельца ресурса:

"SecureSpa.IntegrationTests": {
  "Profile": "IdentityServerSPA",
  "AllowedGrantTypes": [ "password" ],
  "ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ],
  "AllowedScopes": [ "SecureSpaAPI", "openid", "profile" ]
}

Затем в WeatherForecastControllerTests.cs я пытаюсь запросить токен следующим образом:

var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
    Address = disco.TokenEndpoint,
    ClientId = "SecureSpa.IntegrationTests",
    ClientSecret = "secret",

    Scope = "SecureSpaAPI openid profile",
    UserName = "demouser@securespa",
    Password = "Pass@word1"
  });

При запуске теста я пробовал много разных комбинаций, однако результаты обычно совпадают (unauthorized_client). Это соответствующий вывод журнала от Identity Server:

IdentityServer4.Endpoints.TokenEndpoint: Debug: Start token request.
IdentityServer4.Validation.ClientSecretValidator: Debug: Start client validation
IdentityServer4.Validation.BasicAuthenticationSecretParser: Debug: Start parsing Basic Authentication secret
IdentityServer4.Validation.PostBodySecretParser: Debug: Start parsing for secret in post body
IdentityServer4.Validation.SecretParser: Debug: Parser found secret: PostBodySecretParser
IdentityServer4.Validation.SecretParser: Debug: Secret id found: SecureSpa.IntegrationTests
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SecureSpa.IntegrationTests succeeded.
IdentityServer4.Validation.ClientSecretValidator: Debug: Public Client - skipping secret validation success
IdentityServer4.Validation.ClientSecretValidator: Debug: Client validation success
IdentityServer4.Events.DefaultEventService: Information: {
  "Name": "Client Authentication Success",
  "Category": "Authentication",
  "EventType": "Success",
  "Id": 1010,
  "ClientId": "SecureSpa.IntegrationTests",
  "AuthenticationMethod": "SharedSecret",
  "ActivityId": "0HLPN4PPDDMCJ",
  "TimeStamp": "2019-09-12T02:10:57Z",
  "ProcessId": 28948,
  "LocalIpAddress": "unknown",
  "RemoteIpAddress": "unknown"
}
IdentityServer4.Validation.TokenRequestValidator: Debug: Start token request validation
IdentityServer4.Validation.TokenRequestValidator: Debug: Start resource owner password token request validation
IdentityServer4.Validation.TokenRequestValidator: Error: Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = SecureSpa.IntegrationTests }, details: {
  "ClientId": "SecureSpa.IntegrationTests",
  "ClientName": "SecureSpa.IntegrationTests",
  "GrantType": "password",
  "Raw": {
    "grant_type": "password",
    "username": "demouser@securespa",
    "password": "***REDACTED***",
    "scope": "SecureSpaAPI",
    "client_id": "SecureSpa.IntegrationTests",
    "client_secret": "***REDACTED***"
  }
}
IdentityServer4.Events.DefaultEventService: Information: {
  "Name": "Token Issued Failure",
  "Category": "Token",
  "EventType": "Failure",
  "Id": 2001,
  "ClientId": "SecureSpa.IntegrationTests",
  "ClientName": "SecureSpa.IntegrationTests",
  "Endpoint": "Token",
  "GrantType": "password",
  "Error": "unauthorized_client",
  "ActivityId": "0HLPN4PPDDMCJ",
  "TimeStamp": "2019-09-12T02:10:57Z",
  "ProcessId": 28948,
  "LocalIpAddress": "unknown",
  "RemoteIpAddress": "unknown"
}
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 212.96790000000001ms 400 application/json; charset=UTF-8

Поддерживается ли такой подход? Если нет, есть ли альтернативный подход, который можно использовать для получения токена для написания интеграционных тестов? Я планирую настроить тестовых пользователей вместе с тестовым клиентом, чтобы я мог тестировать множество различных вариантов поведения.


person Jason Taylor    schedule 12.09.2019    source источник


Ответы (1)


Я продолжил работу над этой проблемой и обнаружил, что разрешенный тип пароля не добавлялся, когда для профиля задано значение IdentityServerSPA. Я не видел способа добавить клиента без профиля через appsettings, поэтому я удалил конфигурацию из appsettings и создал клиентов, используя этот подход:

services.AddIdentityServer()
    //.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.Clients.AddIdentityServerSPA("SecureSpa", builder =>
        {
            builder.WithRedirectUri("https://localhost:44307/authentication/login-callback");
            builder.WithLogoutRedirectUri("https://localhost:44307/authentication/logout-callback");
        });
        options.Clients.Add(new Client
        {
            ClientId = "SecureSpa.IntegrationTests",
            AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
            ClientSecrets = { new Secret("secret".Sha256()) },
            AllowedScopes = { "SecureSpaAPI", "openid", "profile" }
        });
    });

Теперь мои тесты выполнены. Здесь вы можете увидеть окончательное решение; https://github.com/JasonGT/SecureSpa/.

Все работает нормально, однако, похоже, есть ошибка (или ограничение функции) в DefaultClientRequestParametersProvider. См. Метод GetClientParameters - если указанный клиент не имеет связанного профиля, выдается InvalidOperationException.

Дайте мне знать, если вам понадобится дополнительная информация.

person Jason Taylor    schedule 12.09.2019