Как сохранить несколько значений ClaimTypes.Role в списке утверждений пользователя?

Я создаю модуль ASP.NET Core (v3.1), и мне только что удалось настроить аутентификацию OpenIdConnect. Теперь мне нужно получить все роли пользователя из API, чтобы предоставить или запретить доступ к ним, затем я добавил несколько значений утверждения к одной и той же роли утверждения «ClaimTypes.Role» в списке утверждений пользователя через событие OnAuthorizationCodeReceived следующим образом:

OnAuthorizationCodeReceived = async (context) =>
{
    // Uses the authentication code and gets the access and refresh token
    var client = new HttpClient();
    var response = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest()
    {
        Address = urlServer + "/connect/token",
        ClientId = "hybrid",

        Code = context.TokenEndpointRequest.Code,
        RedirectUri = context.TokenEndpointRequest.RedirectUri,
    }

    if (response.IsError) throw new Exception(response.Error);

    var identity = new ClaimsIdentity(context.Principal.Identity);

    var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
    listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));

    context.HttpContext.User = new ClaimsPrincipal(identity);

    context.HandleCodeRedemption(response.AccessToken, response.IdentityToken);
}

Во время отладки я заметил, что все роли добавляются в список претензий пользователя после этой строки:

context.HttpContext.User = new ClaimsPrincipal(identity);

Но, по-видимому, в моем домашнем контроллере (именно туда пользователь перенаправляется после аутентификации), когда я получаю доступ к HttpContext.User, я не могу найти ни одну из ролей, которые я добавил ранее, кроме «Администратор» ( я предполагаю, что это значение ClaimTypes.Role по умолчанию).

[Authorize]
public IActionResult Index()
{
    if (User.IsInRole("SomeRole"))
    {
        return RedirectToAction("SomeAction", "SomeController");
    }
    else
    {
        return RedirectToAction("Forbidden", "Error");
    }
}

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

public async Task Login(string returnUrl = "/")
{
    await HttpContext.ChallengeAsync(
        "OIDC",
        new AuthenticationProperties
        {
            AllowRefresh = false,
            IsPersistent = true,
            RedirectUri = returnUrl
        });
}

В некоторых примерах говорилось, что я могу использовать context.Principal.AddIdentity(identity); для сохранения нового списка утверждений, но затем я получил следующую ошибку:

InvalidOperationException: only a single identity supported
IdentityServer4.Hosting.IdentityServerAuthenticationService.AssertRequiredClaims(ClaimsPrincipal principal)

Подводя итог, я должен найти способ сохранить утверждения ролей, которые я добавил в список утверждений пользователя, но до сих пор я не добился успеха.


person Lionet    schedule 20.04.2020    source источник


Ответы (1)


Обновите это, если это кому-то полезно.

Я пришел к выводу, что проблема связана с context.HttpContext.User = new ClaimsPrincipal(identity);, который, как я понял ранее, является частью кода, обрабатывающего постоянство новых требований.

На самом деле, я заметил, что существует атрибут context.Principal типа ClaimsPrincipal, и похоже, что это фактический Текущий контекст, поэтому я покопался в нем и попытался найти способ добавить элементы в его атрибут "IEnumerable<Claim> Claims", который доступен только для чтения.

Через некоторое время я нашел следующее решение, которое отлично сработало для меня:

Вместо

var identity = new ClaimsIdentity(context.Principal.Identity);
var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API
listRoles.ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role)));

Я старался

var identity = context.Principal.Identity as ClaimsIdentity;
if(identity != null)
{
    var listRoles = GenericProxies.RestGet<List<string>>(urlGetRoles, response.AccessToken); // GET request to API 
    foreach (var role in listRoles)
    {
        identity.AddClaim(new Claim(ClaimTypes.Role, role));
    }
}
person Lionet    schedule 22.04.2020