9

IdentityServer3 でリソース所有者フローを使用し、次のように JavaScript でユーザー名とパスワードを使用してトークン取得要求を ID サーバー トークン エンドポイントに送信します。

        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);
    }

ID サーバーからアクセス トークンが返され、ユーザーが認証されます。次に、このトークンを使用して Web Api にリクエストを送信します。

問題は、ユーザーにロールが割り当てられているかどうかを確認すると、クレームが存在しないことがわかります。

    [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" };
    }

クライアントが ID サーバーでどのように定義されるかを次に示します。

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 にクレームを追加するにはどうすればよいですか? どうもありがとう!

4

3 に答える 3

17

私はこれを自分で理解するのにしばらく費やしました。ヤンの答えに対する@leastprivilegeのコメントには手がかりがありました。この答えはそれを拡張しているだけです。
それはすべて、oAuth と OIDC の仕様がどのように進化したかにかかっています。これは、IdentityServer の成果物ではありません (これはすばらしいことです)。まず、ID トークンとアクセス トークンの違いに関するかなり適切な説明があります: https://github.com/IdentityServer/IdentityServer3/issues/2015は一読の価値があります。

リソース オーナー フローでは、あなたが行っているように、常にアクセス トークンを取得します。デフォルトおよび仕様では、そのトークンにクレームを含めるべきではありません (理由については、上記のリンクを参照してください)。しかし、実際には、できる場合は非常に便利です。クライアントとサーバーの両方で余分な労力を節約できます。

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",(まあ、私が見る限り、スコープはここで 2 つの役割を果たします - それらは制御の形式でもあります。つまり、クライアントは特定のスコープを要求しており、そうである場合は拒否することができます)。それらは許可されていませんが、それは別のトピックです)。

しばらくの間、私が見逃していた重要な行に注意してくださいType = ScopeType.Resource(アクセス トークンはリソースへのアクセスを制御するためのものです)。これは、アクセス トークンに適用され、指定されたクレームがトークンに含まれることを意味します (おそらく、仕様に反すると思いますが、素晴らしいことです)。

最後に、私の例では、いくつかの特定の主張IncludeAllClaimsForUserと明らかにばかげている主張の両方を含めましたが、いくつかのオプションを示したかっただけです。

于 2016-02-21T17:06:49.637 に答える
2

IdentityServerServiceFactory のデフォルトの IClaimsProvider を置き換えることで、これを実現できることがわかりました。

カスタマイズされた 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 に追加されます。

于 2015-11-27T04:49:03.020 に答える
0

他の方法を試しただけでなく、スコープなどの可能なすべての組み合わせを試しました。アクセス トークンで読み取ることができたのは「スコープ」、「スコープ名」だけでした。リソース フローについては、期間を追加したクレームはありませんでした。

私はこれをすべてしなければなりませんでした

  1. カスタム UserServiceBase を追加し、AuthenticateLocalAsync をオーバーライドします。そこにユーザー名/パスワードがあり、データベースから取得するために両方が必要なためです。
  2. 同じ関数で必要なクレームを追加します (これ自体ではクレームをアクセス トークンに追加しませんが、さまざまな ClaimsPrincipal パラメーターでそれらを読み取ることができます)
  3. カスタム DefaultClaimsProvider を追加し、GetAccessTokenClaimsAsync をオーバーライドします。ここで、ClaimsPrincipal サブジェクトには以前に設定したクレームが含まれています。それらを取り出して、結果のクレームの ølist に再度入れます。

この最後のステップは、カスタム UserServiceBase で GetProfileDataAsync をオーバーライドすることで行われる可能性があると思いますが、上記はうまく機能したので、気にしませんでした。

一般的な問題は、クレームを設定する方法ではなく、どこにデータを入力するかです。どこかで何かをオーバーライドする必要があります。

データベースからのデータが必要だったので、これは私にとってはうまくいきました。他の誰かが他の場所にクレームを入力する必要があります。ただし、Scopes and Claims Identity Server 構成を適切に設定したからといって、それらが魔法のように表示されるわけではありません。

回答のほとんどは、クレーム値を適切に設定する場所について一言も述べていません。実行した特定のオーバーライドごとに、渡されたパラメーターは、クレームがある場合、関数で IDまたはアクセス トークンにアタッチされます。

それを気にするだけで、すべてうまくいくでしょう。

于 2016-08-18T14:26:34.670 に答える