簡體   English   中英

密碼流的 IdentityServer4 客戶端不包括訪問令牌中的 TestUser 聲明

[英]IdentityServer4 client for Password Flow not including TestUser claims in access token

我正在嘗試使用 IdentityServer4 中的(舊版)資源所有者密碼流創建一個沙盒應用程序。 我用這些包建立了一個全新的 ASP.NET Core 3 項目:

<PackageReference Include="IdentityServer4" Version="3.1.3" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />

我正在使用以下啟動部分:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryApiResources(new[] { new ApiResource("foo-api") })
    .AddInMemoryIdentityResources(new[]
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResources.Email(),
        new IdentityResource("role", new[] { JwtClaimTypes.Role }),
    })
    .AddInMemoryClients(new[]
    {
        new Client
        {
            // Don't use RPO if you can prevent it. We use it here
            // because it's the easiest way to demo with users.
            ClientId = "legacy-rpo",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            AllowAccessTokensViaBrowser = false,
            RequireClientSecret = false,
            AllowedScopes = { "foo-api", "openid", "profile", "email", "role" },

        },
    })
    .AddTestUsers(new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "ABC-123",
            Username = "john",
            Password = "secret",
            Claims = new[]
            {
                new Claim(JwtClaimTypes.Role, "user"),
                new Claim(JwtClaimTypes.Email, "john@example.org"),
                new Claim("x-domain", "foo") },
        },
    })

然后我提供一個 static index.html文件,該文件調用/connect/token端點,如下所示:

const response = await fetch("/connect/token", {
    method: "POST",
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    },
    body: new URLSearchParams({
        "grant_type": "password",
        "client_id": "legacy-rpo",
        "username": "john",
        "password": "secret",
        // scope omitted should net *all* scopes in IDS4
    }),
});

但它返回給我一個 access_token(已解碼),如下所示:

{
  "nbf": 1588582642,
  "exp": 1588586242,
  "iss": "https://localhost:5001",
  "aud": "foo-api",
  "client_id": "legacy-rpo",
  "sub": "ABC-123",
  "auth_time": 1588582642,
  "idp": "local",
  "scope": [
    "email",
    "openid",
    "profile",
    "role",
    "foo-api"
  ],
  "amr": [
    "pwd"
  ]
}

我缺少電子郵件、角色等作為access_token中的頂級條目。

在挖掘源代碼時,我看到TestUsers 的 ProfileService應該通過擴展方法將所有請求的聲明添加到令牌。 我在谷歌搜索我的問題時發現的大多數問題要么是我已經做過的(或嘗試過的,見下文),要么是關於其他邊緣情況。

許多其他線程也導致Dominick Baier 關於角色的帖子,但問題是API 方面不識別角色。 我的問題是該role根本不包含在令牌中。

我試過的:

  • 在各個地方的"role"JwtClaimTypes.Role之間切換。
  • 有和沒有IdentityResources
  • 挖掘 IDS4 代碼庫以找到其背后的邏輯

關於ProfileService的腳注

我試過添加這個:

public class ProfileService : TestUserProfileService
{
    public ProfileService(TestUserStore users, ILogger<TestUserProfileService> logger) 
        : base(users, logger)
    { }

    public override Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var role = context.Subject.FindFirst(ClaimTypes.Role);
        context.IssuedClaims.Add(role);
        return base.GetProfileDataAsync(context);
    }

    public override Task IsActiveAsync(IsActiveContext context)
    {
        return base.IsActiveAsync(context);
    }
}

AddIdentityServer()構建器鏈:

.AddProfileService<ProfileService>()

但是GetProfileDataAsync(...)方法根本沒有被命中,沒有斷點觸發。 所以這表明默認的TestUserProfileService也永遠不會被擊中,從而解釋了我的令牌中缺少聲明。

密碼流是否不支持這可能是因為它是 OAuth2 而不是 OpenID Connect 流?


我錯過了什么? 真的需要創建一個自定義ProfileService來添加所有這些聲明嗎? 我真的覺得TestUser的默認ProfileService應該已經這樣做了?

我最終得到了以下結果(不確定它是解決方案還是解決方法),對於未來的訪問者來說是什么價值:

new ApiResource("foo-api")
{
    Scopes =
    {
        new Scope("foo-api.with.roles", new[] { "role" }),
    }
}
new Client
{
    // Don't use RPO if you can prevent it. We use it here
    // because it's the easiest way to demo with users.
    ClientId = "legacy-rpo",
    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
    AllowAccessTokensViaBrowser = false,
    RequireClientSecret = false,
    AllowedScopes = { "foo-api", "foo-api.with.roles" },
}
new TestUser
{
    SubjectId = "EFG-456",
    Username = "mary",
    Password = "secret",
    Claims = { new Claim("role", "editor") },
}

然后像這樣檢索令牌:

const response = await fetch("/connect/token", {
    method: "POST",
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    },
    body: new URLSearchParams({
        "grant_type": "password",
        "client_id": "legacy-rpo",
        "username": "mary",
        "password": "secret",
    }),
});

const json = await response.json();

console.log(json);

您應該能夠克隆我的sample-asp-net-core-auth-policies存儲庫並開箱即用地運行它以查看它的工作情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM