Использование авторизации ролей ASP.NET с неявным потоком IdentityServer3

Мое одностраничное приложение использует OidcTokenManager для подключения к IdentityServer3 STS с использованием неявного потока. Клиент представляет токен доступа IDS3 веб-службе ASP.NET Core (WebApi) как токен-носитель; приложение веб-службы настроено для использования промежуточного программного обеспечения IDS3 и ограничивает доступ к своим методам с помощью атрибута авторизации.

Конфигурация клиента SPA:

function configureTokenManager() {
    console.log("configureTokenManager()");
    var config = {
        authority: $config.authority,
        client_id: "BNRegistry",
        redirect_uri: $config.webRoot + "/#/authorised/",  
        post_logout_redirect_uri: $config.webRoot + "/#/",

        response_type: "id_token token",
        scope: "openid profile email BNApi",

        silent_redirect_uri: $config.webRoot + "/#/renew/",
        silent_renew: true,

        filter_protocol_claims: false
    };
    return new OidcTokenManager(config);
};

Конфигурация области в STS:

new Scope
    {
        Name = "BNApi",
        DisplayName = "BN Api",
        Enabled = true,
        Type = ScopeType.Resource,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim(Constants.ClaimTypes.Name),
            new ScopeClaim(Constants.ClaimTypes.Role)
        }
    }

Конфигурация WebApi:

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = Configuration["Authority"],
        RequiredScopes = new[] {"BNApi"},
        NameClaimType = IdentityModel.JwtClaimTypes.Name,                
        RoleClaimType = IdentityModel.JwtClaimTypes.Role
    });

Метод WebApi:

[Authorize]
public IActionResult Get()
{
    ...
}

Это работает, как ожидалось, отклоняя неаутентифицированного пользователя с 401. Если я исследую утверждения для пользователя в методе контроллера api (например, User.Claims.ToList()), он содержит записи для любых ролей, которым был назначен пользователь.

Однако, если я проверяю свойство User.Identity.Name, оно всегда равно нулю, а если я запрашиваю User.IsInRole("Administrator"), оно всегда ложно, даже когда пользователю назначена эта роль. Кроме того, если я добавлю имя роли к атрибуту Authorize ([Authorize(Role="Administrator")]), пользователи будут отклонены с кодом 401 независимо от того, принадлежат они к указанной роли или нет.

Как я могу заставить IdentityServer3 нормально работать с авторизацией ролей ASP.NET?


person Paul Taylor    schedule 07.03.2016    source источник


Ответы (1)


Вы пробовали сбросить InboundClaimTypeMap?

На странице документации IdentityServer3 здесь:

Когда вы просматриваете утверждения на странице about, вы заметите две вещи: некоторые утверждения имеют нечетные длинные имена типов и их больше, чем вам, вероятно, нужно в вашем приложении.

Длинные имена утверждений исходят от обработчика Microsoft JWT, который пытается сопоставить некоторые типы утверждений с типами классов .NET ClaimTypes.

К сожалению, это сопоставление приводит к нарушению конкретных имен утверждений, которые вы определили как name и role, потому что их имена преобразуются и больше не соответствуют тому, что вы ожидали. Это приводит к тому, что [Authorize(Roles = "")] и User.IsInRole("") не работают должным образом.

В свой API Startup.cs вы должны добавить следующее:

JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions()
{
    ...
});

РЕДАКТИРОВАТЬ: Информация ниже неверна !. Как указал @Paul Taylor, «свойство AlwaysInclude гарантирует, что соответствующее утверждение всегда присутствует в токене идентификации (который используется с клиентом, а не API). Это область ресурса, поэтому свойство не имеет никакого эффекта». Спасибо, что помогли мне немного понять, как работает IdentityServer :-)

Чтобы утверждения Name и Role были включены при доступе к API, вам необходимо специально пометить их как alwaysInclude в вашем списке ScopeClaim.

new Scope
    {
        Name = "BNApi",
        DisplayName = "BN Api",
        Enabled = true,
        Type = ScopeType.Resource,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim(Constants.ClaimTypes.Name, true), //<-- Add true here
            new ScopeClaim(Constants.ClaimTypes.Role, true) // and here!
        }
    }

person Jamie Dunstan    schedule 08.03.2016
comment
Однако, спасибо @Jamie Dunstan, свойство AlwaysInclude гарантирует, что соответствующее утверждение всегда присутствует в токене идентификации (который используется с клиентом, а не с API). Это область ресурса, поэтому свойство не действует. - person Paul Taylor; 08.03.2016
comment
Ах, мои извинения. Я настроил свой таким образом и предположил, что это решение. Вы пробовали сбросить InboundClaimTypeMap? т.е. добавить JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>(); Прямо перед app.UseIdentityServerBearerTokenAuthentication(... в свой API Startup.cs? - person Jamie Dunstan; 08.03.2016
comment
Спасибо! Это включило функции IsInRole (администратор) и [Авторизация (роли = администратор)]. К сожалению, свойство User.Identity.Name все еще пустое. Любые идеи? У вас это работает в вашем приложении? - person Paul Taylor; 09.03.2016
comment
В любом случае, если вы обновите свой пост, добавив это и отметив вводящий в заблуждение бит, я помечу его как ответ. - person Paul Taylor; 09.03.2016
comment
@PaulTaylor - Спасибо за вашу помощь! Я рад, что функция вашей роли теперь работает. СОВМЕСТНАЯ ДЕЯТЕЛЬНОСТЬ!!!! :-) - person Jamie Dunstan; 10.03.2016
comment
@PaulTaylor - Мой User.Identity.Name действительно работает, да. Я предполагаю, что у вас есть претензия под названием имя? Если да, то как добавить это утверждение? - person Jamie Dunstan; 10.03.2016
comment
@JamieDunstanMar, я этого не сделал, и в этом была проблема. Большое спасибо. - person Paul Taylor; 15.03.2016