【发布时间】: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