Как добавить заявки на токен доступа, полученный от IdentityServer3, используя поток владельца ресурса с клиентом javascript

Я использую поток владельца ресурса с IdentityServer3 и отправляю запрос на получение токена конечной точке токена идентификационного сервера с именем пользователя и паролем в javascript, как показано ниже:

        function getToken() {
        var uid = document.getElementById("username").value;
        var pwd = document.getElementById("password").value;
        var xhr = new XMLHttpRequest();
        xhr.onload = function (e) {
            console.log(xhr.status);
            console.log(xhr.response);
            var response_data = JSON.parse(xhr.response);
            if (xhr.status === 200 && response_data.access_token) {
                getUserInfo(response_data.access_token);
                getValue(response_data.access_token);
            }
        }
        xhr.open("POST", tokenUrl);
        var data = {
            username: uid,
            password: pwd,
            grant_type: "password",
            scope: "openid profile roles",
            client_id: 'client_id'
        };
        var body = "";
        for (var key in data) {
            if (body.length) {
                body += "&";
            }
            body += key + "=";
            body += encodeURIComponent(data[key]);
        }
        xhr.setRequestHeader("Authorization", "Basic " + btoa(client_id + ":" + client_secret));
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.send(body);
    }

Токен доступа возвращается с сервера идентификации, и пользователь проходит аутентификацию. Затем я использую этот токен для отправки запроса на мой веб-интерфейс.

Проблема в том, что когда я проверяю, назначена ли пользователю роль, я обнаруживаю, что претензии не существует.

    [Authorize]
    // GET api/values
    public IEnumerable<string> Get()
    {
        var id = RequestContext.Principal as ClaimsPrincipal;
        bool geek = id.HasClaim("role", "Geek");  // false here
        bool asset_mgr = id.HasClaim("role", "asset_manager"); // false here
        return new string[] { "value1", "value2" };
    }

Вот как определяется клиент на сервере идентификации.

new Client 
            {
                ClientName = "Client",
                ClientId = "client_id",
                Flow = Flows.ResourceOwner,
                RequireConsent = false,
                AllowRememberConsent = false,

                AllowedScopes = new List<string>
                {
                    "openid",
                    "profile",
                    "roles",
                    "sampleApi"
                },
                AbsoluteRefreshTokenLifetime = 86400,
                SlidingRefreshTokenLifetime = 43200,
                RefreshTokenUsage = TokenUsage.OneTimeOnly,
                RefreshTokenExpiration = TokenExpiration.Sliding,
                ClientSecrets = new List<Secret>
                {
                    new Secret("4C701024-0770-4794-B93D-52B5EB6487A0".Sha256())
                },
            },

и вот как определяется пользователь:

new InMemoryUser
            {
                Username = "bob",
                Password = "secret",
                Subject = "1",

                Claims = new[]
                {
                    new Claim(Constants.ClaimTypes.GivenName, "Bob"),
                    new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
                    new Claim(Constants.ClaimTypes.Role, "Geek"),
                    new Claim(Constants.ClaimTypes.Role, "Foo")
                }
            }

Как я могу добавить претензии к access_token в этом случае? Большое спасибо!

3 ответа

Я просто потратил некоторое время, чтобы понять это сам. Комментарий @lessprivilege к ответу Янга имел ключ, этот ответ только расширяется.
Все зависит от того, как эволюционировали спецификации oAuth и OIDC, это не артефакт IdentityServer (что потрясающе). Во-первых, довольно приличное обсуждение различий между токенами идентификации и токенами доступа: https://github.com/IdentityServer/IdentityServer3/issues/2015 которое стоит прочитать.

С потоком Resource Owner, как и вы, вы всегда получите токен доступа. По умолчанию и согласно спецификации, вы не должны включать претензии в этот токен (см. Ссылку выше, чтобы узнать почему). Но на практике очень приятно, когда ты можешь; это экономит ваши дополнительные усилия как на клиенте, так и на сервере.

Leastprivilege имеет в виду, что вам нужно создать область видимости, что-то вроде этого:

new Scope
{
    Name = "member",
    DisplayName = "member",
    Type = ScopeType.Resource,

    Claims = new List<ScopeClaim>
        {
              new ScopeClaim("role"),
              new ScopeClaim(Constants.ClaimTypes.Name),
              new ScopeClaim(Constants.ClaimTypes.Email)
        },

    IncludeAllClaimsForUser = true
}

И тогда вам нужно запросить эту область, когда вы запрашиваете токен. Т.е. твоя линияscope: "openid profile roles", должен измениться на scope: "member", (ну, я говорю, что - насколько я вижу, области играют здесь двойную роль - они также являются формой контроля, то есть клиент запрашивает определенные области и может быть отклонен, если это не разрешено, но это другая тема).

Обратите внимание на важную строку, которая ускользнула от меня на некоторое время, Type = ScopeType.Resource (потому что токены доступа предназначены для управления доступом к ресурсам). Это означает, что он будет применяться к токенам доступа, и указанные требования будут включены в токен (я думаю, возможно, против спецификации, но замечательно).

Наконец, в моем примере я включил как некоторые конкретные утверждения, так и IncludeAllClaimsForUser что, очевидно, глупо, но просто хотел показать вам несколько вариантов.

Я нахожу, что могу этого добиться, заменив IClaimsProvider по умолчанию на IdentityServerServiceFactory.

Cusomized IClaimsProvider, как показано ниже:

public class MyClaimsProvider : DefaultClaimsProvider
{
    public MaccapClaimsProvider(IUserService users) : base(users)
    {
    }

    public override Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, ValidatedRequest request)
    {
        var baseclaims = base.GetAccessTokenClaimsAsync(subject, client, scopes, request);

        var claims = new List<Claim>();
        if (subject.Identity.Name == "bob")
        {
            claims.Add(new Claim("role", "super_user"));
            claims.Add(new Claim("role", "asset_manager"));
        }

        claims.AddRange(baseclaims.Result);

        return Task.FromResult(claims.AsEnumerable());
    }

    public override Task<IEnumerable<Claim>> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, bool includeAllIdentityClaims, ValidatedRequest request)
    {
        var rst = base.GetIdentityTokenClaimsAsync(subject, client, scopes, includeAllIdentityClaims, request);
        return rst;
    }
}

Затем замените IClaimsProvider следующим образом:

// custom claims provider
factory.ClaimsProvider = new Registration<IClaimsProvider>(typeof(MyClaimsProvider));

В результате, когда запрос на токен доступа отправляется на конечную точку токена, заявки добавляются в access_token.

Мало того, что я пробовал другие методы, я пробовал все возможные комбинации областей и т. Д. Все, что я мог прочитать в маркере доступа, это "область действия", "имя области действия", для потока ресурсов не было никаких претензий, которые я добавил.

Я должен был сделать все это

  1. Добавьте пользовательскую UserServiceBase и переопределите AuthenticateLocalAsync, поскольку у меня есть имя пользователя и пароль, и мне нужно и то, и другое для извлечения данных из базы данных.
  2. Добавьте утверждения, которые мне нужны, в той же функции (это само по себе не добавит утверждения в Access Token, однако вы сможете прочитать их в различных параметрах ClaimsPrincipal)
  3. Добавьте пользовательский DefaultClaimsProvider и переопределите GetAccessTokenClaimsAsync, где тема ClaimsPrincipal содержит ранее установленные утверждения, я просто извлекаю их и снова помещаю в список утверждений для результата.

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

Общая проблема не в том, как устанавливать претензии, а в том, где вы их заполняете. Вы должны где-то переопределить.

Это сработало для меня, так как мне нужны были данные из базы данных, кто-то другой должен заполнять заявки в другом месте. Но они не появятся волшебным образом только потому, что вы правильно настроили конфигурации Scopes и Claims Identity Server.

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

Просто позаботься об этом, и все будет хорошо.

Другие вопросы по тегам