[英]IdentityServer4 client for Password Flow not including TestUser claims in access token
I'm trying to create a sandbox application, using the (legacy) Resource Owner Password flow in IdentityServer4.我正在尝试使用 IdentityServer4 中的(旧版)资源所有者密码流创建一个沙盒应用程序。 I've set up a brand new ASP.NET Core 3 project with these packages:我用这些包建立了一个全新的 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" />
I'm using the following startup sections:我正在使用以下启动部分:
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") },
},
})
And then I serve a static index.html
file that calls the /connect/token
endpoint like this:然后我提供一个 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
}),
});
But it returns me an access_token that (decoded) looks like this:但它返回给我一个 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"
]
}
I'm missing e-mail, role, etc. as top-level entries in the access_token
.我缺少电子邮件、角色等作为access_token
中的顶级条目。
When digging through the source code, I see that the ProfileService for TestUsers should add all requested claims to the token via an extension method .在挖掘源代码时,我看到TestUsers 的 ProfileService应该通过扩展方法将所有请求的声明添加到令牌。 Most questions I found while googling my problem either do what I already do (or tried, see below), or are about other edge cases.我在谷歌搜索我的问题时发现的大多数问题要么是我已经做过的(或尝试过的,见下文),要么是关于其他边缘情况。
Many other threads also lead to Dominick Baier's post on roles , but there the problem is that the API side doesn't recognize the role.许多其他线程也导致Dominick Baier 关于角色的帖子,但问题是API 方面不识别角色。 My problem is that the role
isn't included at all in the token.我的问题是该role
根本不包含在令牌中。
What I've tried:我试过的:
"role"
and JwtClaimTypes.Role
in various places.在各个地方的"role"
和JwtClaimTypes.Role
之间切换。IdentityResources
有和没有IdentityResources
Footnote about ProfileService
关于ProfileService
的脚注
I've tried adding this:我试过添加这个:
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);
}
}
to the AddIdentityServer()
builder chain:到AddIdentityServer()
构建器链:
.AddProfileService<ProfileService>()
but the GetProfileDataAsync(...)
method isn't being hit at all, no breakpoint triggers.但是GetProfileDataAsync(...)
方法根本没有被命中,没有断点触发。 So that would suggest that the default TestUserProfileService
would also never be hit, thus explaining the lack of claims in my tokens.所以这表明默认的TestUserProfileService
也永远不会被击中,从而解释了我的令牌中缺少声明。
Is this not supported in Password Flow perhaps because it's an OAuth2 and not an OpenID Connect flow?密码流是否不支持这可能是因为它是 OAuth2 而不是 OpenID Connect 流?
What am I missing?我错过了什么? Do I really need to create a custom ProfileService
to add all these claims?我真的需要创建一个自定义ProfileService
来添加所有这些声明吗? I really felt the default ProfileService
for TestUser
s should do this already??我真的觉得TestUser
的默认ProfileService
应该已经这样做了?
I ended up with the following (not sure if it's a solution or a workaround), for what it's worth to future visitors:我最终得到了以下结果(不确定它是解决方案还是解决方法),对于未来的访问者来说是什么价值:
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") },
}
And then retrieving tokens like this:然后像这样检索令牌:
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);
You should be able to clone my sample-asp-net-core-auth-policies
repository and run it out of the box to see this at work.您应该能够克隆我的sample-asp-net-core-auth-policies
存储库并开箱即用地运行它以查看它的工作情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.