【问题标题】:IdentityServer4 client for Password Flow not including TestUser claims in access token密码流的 IdentityServer4 客户端不包括访问令牌中的 TestUser 声明
【发布时间】:2020-07-21 19:40:12
【问题描述】:

我正在尝试使用 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") },
        },
    })

然后我提供一个静态的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 中缺少电子邮件、角色等作为顶级条目。

在挖掘源代码时,我看到the ProfileService for TestUsers 应该通过an extension method 将所有请求的声明添加到令牌中。我在谷歌上搜索我的问题时发现的大多数问题要么是我已经做过的(或尝试过的,见下文),要么是关于其他极端情况。

许多其他线程也导致Dominick Baier's post on roles,但问题是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 流?


我错过了什么?我真的需要create a custom ProfileService 来添加所有这些声明吗?我真的觉得the default ProfileService for TestUsers 应该已经这样做了??

【问题讨论】:

  • 曾经在Client 周围有一个设置在AlwaysIncludeUserClaimsInIdToken 或类似的东西......没有它你不会在默认情况下获得令牌内的声明,所以现在需要调用身份服务器 4 中的 /userinfo 端点以获取该数据
  • 嗯,我想我已经尝试过了,可能会再次这样做以仔细检查它没有解决问题。该设置确实谈到了 IdToken ,但我希望声明位于访问令牌中。 - 不知何故,我使用 EF Core 和 ASP.NET Identity 的真实应用程序确实“神奇地”包含角色,所以也许我应该检查他们的源代码...
  • 我可能有一些解决方案的开始,但会推迟发布,因为没有任何答案的问题会得到更多关注,我很想看看其他人的见解。 ..
  • 对于资源所有者流程,您可以通过IResourceOwnerPasswordValidator 接口挂钩到令牌声明
  • @CeylanMumunKocabaş 我找到了一种方法来获得我需要的东西,但不确定它是解决方案还是解决方法。我已将其作为答案分享。

标签: c# asp.net-core oauth-2.0 identityserver4 openid


【解决方案1】:

我最终得到了以下结果(不确定这是一个解决方案还是一种变通方法),对于未来的访问者来说有什么价值:

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

您应该能够clone my sample-asp-net-core-auth-policies repository 并开箱即用地运行它以查看此功能。

【讨论】:

    猜你喜欢
    • 2020-12-07
    • 2017-05-01
    • 2021-12-27
    • 2021-06-11
    • 1970-01-01
    • 2017-12-07
    • 2021-10-01
    • 2023-01-11
    • 1970-01-01
    相关资源
    最近更新 更多