IdentityServer3でリソース所有者フローを使用し、以下のようにjavascriptのユーザー名とパスワードを使用してgetトークン要求を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サーバーから返され、ユーザーが認証されます。次に、このトークンを使用してWebApiにリクエストを送信します。
問題は、ユーザーに役割が割り当てられているかどうかを確認すると、クレームが存在しないことです。
[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にクレームを追加するにはどうすればよいですか?どうもありがとう!
私はこれを自分で理解するのに少し時間を費やしました。ヤンの答えに対する@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",
に変更する必要があります(まあ、私が見る限り、スコープはここで二重の役割を果たします-それらは制御の形式でもあります。つまり、クライアントは要求しています特定のスコープであり、許可されていない場合は拒否できますが、それは別のトピックです)。
しばらくの間私を避けていた重要な行であるType = ScopeType.Resource
に注意してください(アクセストークンはリソースへのアクセスを制御するためのものであるため)。これは、アクセストークンに適用され、指定されたクレームがトークンに含まれることを意味します(おそらく、仕様に反しますが、すばらしいと思います)。
最後に、私の例では、いくつかの特定の主張とIncludeAllClaimsForUser
の両方を含めましたが、これは明らかにばかげていますが、いくつかのオプションを示したかっただけです。
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に追加されます。
他の方法を試しただけでなく、スコープなどの可能なすべての組み合わせを試しました。アクセストークンで読み取れるのは「スコープ」、「スコープ名」だけでした。リソースフローの場合、期間を追加したクレームはありませんでした。
私はこれをすべてしなければなりませんでした
この最後のステップは、カスタムUserServiceBaseのGetProfileDataAsyncをオーバーライドして実行される可能性があると思いますが、上記はうまく機能したので、気にしませんでした。
一般的な問題は、クレームをどのように設定するかではなく、どこに入力するかです。どこかで何かをオーバーライドする必要があります。
データベースからのデータが必要だったので、これは私にとってはうまくいきました。他の誰かがクレームを他の場所に入力する必要があります。ただし、スコープとクレームIDサーバーの構成を適切に設定したからといって、魔法のように表示されることはありません。
ほとんどの回答は、クレーム値を適切に設定するためのwhereについての言葉ではないと言っています。実行した特定のオーバーライドごとに、関数で渡されたパラメーターは、クレームがある場合、ID またはアクセストークンにアタッチされます。
気をつければ大丈夫です。