IdentityServer4 - 访问令牌仅包含子声明
IdentityServer4 - access token only contains sub claim
我终于能够从 IdentityServer 获得访问令牌,。
然而,我从 IdentityServer 获得的访问令牌似乎只包含 sub
声明。
这是一个示例令牌
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCOTBDN0JBNkExMjI2RjEyMEU0QzJGOEQzMjIwMzAxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MzcwNTI5MDIsImV4cCI6MTYzNzEzOTMwMiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNTkiLCJhdWQiOiJ3ZWF0aGVyZm9yZWNhc3RzIiwiY2xpZW50X2lkIjoiU3NvQXBwbGljYXRpb25DbGllbnQiLCJjZW50cmFsLXRoZWNsaWVudCI6IlRoZSBTU08gY2xpZW50Iiwic3ViIjoiYTUxYjBkZWMtODQyYS00ZWMyLTgwMGEtMzRmYWQyNTRjZTBlIiwiYXV0aF90aW1lIjoxNjM3MDUyMDcyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkQ3Rjc2MzgwQzNERDkzMzVERDVFMTE1NjE4MDkzNEUwIiwic2lkIjoiOEY1OUZFMjRDNkY1NTM2ODhEQzVCMTM5QjNFQUU1MTMiLCJpYXQiOjE2MzcwNTI5MDIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJlbWFpbCIsIndlYXRoZXJmb3JlY2FzdHMucmVhZCIsIndlYXRoZXJmb3JlY2FzdHMud3JpdGUiXSwiYW1yIjpbInB3ZCJdfQ.MiDmgc7AzbVpogbp8ID3WvJ0eo4a30_taxR9EI9ylyJASSdOiNSsk-sGuW-YnJIzf668EQGkTym6FMIvOyTxem9DxxIs8nI_rboHLvuvj4e7CtJeELwbZyraZtAxjVjm9tn0BVRZxuskzb6XSq4xGrt2ag_E0Re5MeQOjtyL0EeMS5md5IEywfD7ThH7pIu8SofFvV5tAYbwO-OPd5YyqpPGKXslRtFlyc7lj9faQh-e2CRMql5rSwhJRqCiaIaLxvXk8ZwISfdhmuyzHA88xrzXkqTK_RElhq4PY_GqpRe64nMvIBrkSeoOGLzlQNE9wa58UypZFFV4l8Cpy3_P2Q
这是解码后的令牌
我应该能够使用此令牌调用 /connect/userinfo
端点
我可以在输出中看到这个 window:
IdentityServer4.Hosting.EndpointRouter: Debug: Request path /connect/userinfo matched to endpoint type Userinfo
IdentityServer4.Hosting.EndpointRouter: Debug: Endpoint enabled: Userinfo, successfully created handler: IdentityServer4.Endpoints.UserInfoEndpoint
IdentityServer4.Hosting.IdentityServerMiddleware: Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: Start userinfo request
IdentityServer4.Validation.BearerTokenUsageValidator: Debug: Bearer token found in header
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo request validator: IdentityServer4.Validation.UserInfoRequestValidator
IdentityServer4.Validation.TokenValidator: Trace: Start access token validation
IdentityServer4.EntityFramework.Stores.ClientStore: Debug: SsoApplicationClient found in database: True
IdentityServer4.Stores.ValidatingClientStore: Trace: Calling into client configuration validator: IdentityServer4.Validation.DefaultClientConfigurationValidator
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SsoApplicationClient succeeded.
IdentityServer4.EntityFramework.Stores.ClientStore: Debug: SsoApplicationClient found in database: True
IdentityServer4.Stores.ValidatingClientStore: Trace: Calling into client configuration validator: IdentityServer4.Validation.DefaultClientConfigurationValidator
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SsoApplicationClient succeeded.
IdentityServer4.Validation.TokenValidator: Debug: Calling into custom token validator: IdentityServer4.Validation.DefaultCustomTokenValidator
IdentityServer4.Validation.TokenValidator: Debug: Token validation success
{
"ValidateLifetime": true,
"AccessTokenType": "Jwt",
"ExpectedScope": "openid",
"JwtId": "2FB8F8A941528DAF18D8C523BCC9A770",
"Claims": {
"nbf": 1637062004,
"exp": 1637148404,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637060908,
"idp": "local",
"jti": "2FB8F8A941528DAF18D8C523BCC9A770",
"sid": "75C294FC15A544FE60E361B495EE5BCA",
"iat": 1637062004,
"scope": [
"openid",
"profile",
"email",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": "pwd"
}
}
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo response generator: IdentityServer4.ResponseHandling.UserInfoResponseGenerator
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Creating userinfo response
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Scopes in access token: openid profile email weatherforecasts.read weatherforecasts.write
IdentityServer4.EntityFramework.Stores.ResourceStore: Debug: Found openid, profile, email identity scopes in database
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Requested claim types:
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Information: Profile service returned the following claim types:
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: End userinfo request
您甚至可以在令牌验证期间看到我的自定义专用客户端声明。
但是只有 sub 声明从 identityserver userinfo 端点返回...
我该如何解决这个问题?
编辑
我添加了 ProfileService
,它正在被调用。但正如 Dejan 已经提到的,RequestedClaimTypes
是空的。但是我通过添加
更新了数据库
[{"type":"email","identityResourceId":1003},{"type":"sub","identityResourceId":1003}]
记录到 IdentityResourceClaim
table。在输出 window 中,我现在可以看到 email
and sub
claims are requested:
IdentityServer4.Validation.TokenValidator: Debug: Token validation success
{
"ValidateLifetime": true,
"AccessTokenType": "Jwt",
"ExpectedScope": "openid",
"JwtId": "C37AF164BF3A7DE6A28FA7538683248F",
"Claims": {
"nbf": 1637069285,
"exp": 1637155685,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637067659,
"idp": "local",
"email": "pieterjan@example.com",
"name": "Pieterjan",
"jti": "C37AF164BF3A7DE6A28FA7538683248F",
"sid": "BBFA9FD0A06824FA4E982DB1D3669A86",
"iat": 1637069285,
"scope": [
"openid",
"profile",
"email",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": "pwd"
}
}
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo response generator: IdentityServer4.ResponseHandling.UserInfoResponseGenerator
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Creating userinfo response
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Scopes in access token: openid profile email weatherforecasts.read weatherforecasts.write
IdentityServer4.EntityFramework.Stores.ResourceStore: Debug: Found openid, profile, email identity scopes in database
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Requested claim types: email sub
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Information: Profile service returned the following claim types: email name
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: End userinfo request
IdentityServer4.Hosting.IdentityServerMiddleware: Trace: Invoking result: IdentityServer4.Endpoints.Results.UserInfoResult
代码终于进入了ExternalLoginCallback
,但是await signInManager.GetExternalLoginInfoAsync()
returns null now.
来自https://localhost:44359/connect/userinfo
的回复是
{
"email": "pieterjan@example.com",
"name": "Pieterjan",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8"
}
userinfo 端点从 IdentityServer4.Services.IProfileService
调用 GetProfileDataAsync
以获取请求的声明值。因此,简单的解决方案是实施该服务。
假设您将用户管理器定义为 IMyUserManager
并且它具有此处引用的方法(GetClaimsForUser
和 IsActive
),简化的实现可能如下所示:
public class MyProfileService : IProfileService
{
private readonly IMyUserManager userManager;
public MyProfileService(IMyUserManager usermanager)
{
this.userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// context.RequestedClaimTypes will contain the claims you requested when invoking the token endpoint
var myClaims = await userManager.GetClaimsForUser(context.Subject, context.RequestedClaimTypes);
context.IssuedClaims = myClaims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = await userManager.IsActive(context.Subject);
}
}
然后您需要配置 IdentityServer4 以使用此实现,方法是在启动时调用 AddProfileService
,如下所示:
services.AddIdentityServer()
// ...
.AddProfileService<MyProfileService>();
我还应该补充一点,为了让您想要的声明出现在 context.RequestedClaimTypes
中,它需要与您在调用令牌端点时直接请求的范围相关联。
启动时,在配置 IdentityServer4 时,确保您提供给 AddIdentityResources
调用的身份资源包含所需的声明。
我终于能够从 IdentityServer 获得访问令牌,
然而,我从 IdentityServer 获得的访问令牌似乎只包含 sub
声明。
这是一个示例令牌
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCOTBDN0JBNkExMjI2RjEyMEU0QzJGOEQzMjIwMzAxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MzcwNTI5MDIsImV4cCI6MTYzNzEzOTMwMiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNTkiLCJhdWQiOiJ3ZWF0aGVyZm9yZWNhc3RzIiwiY2xpZW50X2lkIjoiU3NvQXBwbGljYXRpb25DbGllbnQiLCJjZW50cmFsLXRoZWNsaWVudCI6IlRoZSBTU08gY2xpZW50Iiwic3ViIjoiYTUxYjBkZWMtODQyYS00ZWMyLTgwMGEtMzRmYWQyNTRjZTBlIiwiYXV0aF90aW1lIjoxNjM3MDUyMDcyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkQ3Rjc2MzgwQzNERDkzMzVERDVFMTE1NjE4MDkzNEUwIiwic2lkIjoiOEY1OUZFMjRDNkY1NTM2ODhEQzVCMTM5QjNFQUU1MTMiLCJpYXQiOjE2MzcwNTI5MDIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJlbWFpbCIsIndlYXRoZXJmb3JlY2FzdHMucmVhZCIsIndlYXRoZXJmb3JlY2FzdHMud3JpdGUiXSwiYW1yIjpbInB3ZCJdfQ.MiDmgc7AzbVpogbp8ID3WvJ0eo4a30_taxR9EI9ylyJASSdOiNSsk-sGuW-YnJIzf668EQGkTym6FMIvOyTxem9DxxIs8nI_rboHLvuvj4e7CtJeELwbZyraZtAxjVjm9tn0BVRZxuskzb6XSq4xGrt2ag_E0Re5MeQOjtyL0EeMS5md5IEywfD7ThH7pIu8SofFvV5tAYbwO-OPd5YyqpPGKXslRtFlyc7lj9faQh-e2CRMql5rSwhJRqCiaIaLxvXk8ZwISfdhmuyzHA88xrzXkqTK_RElhq4PY_GqpRe64nMvIBrkSeoOGLzlQNE9wa58UypZFFV4l8Cpy3_P2Q
这是解码后的令牌
我应该能够使用此令牌调用 /connect/userinfo
端点
我可以在输出中看到这个 window:
IdentityServer4.Hosting.EndpointRouter: Debug: Request path /connect/userinfo matched to endpoint type Userinfo
IdentityServer4.Hosting.EndpointRouter: Debug: Endpoint enabled: Userinfo, successfully created handler: IdentityServer4.Endpoints.UserInfoEndpoint
IdentityServer4.Hosting.IdentityServerMiddleware: Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: Start userinfo request
IdentityServer4.Validation.BearerTokenUsageValidator: Debug: Bearer token found in header
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo request validator: IdentityServer4.Validation.UserInfoRequestValidator
IdentityServer4.Validation.TokenValidator: Trace: Start access token validation
IdentityServer4.EntityFramework.Stores.ClientStore: Debug: SsoApplicationClient found in database: True
IdentityServer4.Stores.ValidatingClientStore: Trace: Calling into client configuration validator: IdentityServer4.Validation.DefaultClientConfigurationValidator
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SsoApplicationClient succeeded.
IdentityServer4.EntityFramework.Stores.ClientStore: Debug: SsoApplicationClient found in database: True
IdentityServer4.Stores.ValidatingClientStore: Trace: Calling into client configuration validator: IdentityServer4.Validation.DefaultClientConfigurationValidator
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SsoApplicationClient succeeded.
IdentityServer4.Validation.TokenValidator: Debug: Calling into custom token validator: IdentityServer4.Validation.DefaultCustomTokenValidator
IdentityServer4.Validation.TokenValidator: Debug: Token validation success
{
"ValidateLifetime": true,
"AccessTokenType": "Jwt",
"ExpectedScope": "openid",
"JwtId": "2FB8F8A941528DAF18D8C523BCC9A770",
"Claims": {
"nbf": 1637062004,
"exp": 1637148404,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637060908,
"idp": "local",
"jti": "2FB8F8A941528DAF18D8C523BCC9A770",
"sid": "75C294FC15A544FE60E361B495EE5BCA",
"iat": 1637062004,
"scope": [
"openid",
"profile",
"email",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": "pwd"
}
}
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo response generator: IdentityServer4.ResponseHandling.UserInfoResponseGenerator
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Creating userinfo response
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Scopes in access token: openid profile email weatherforecasts.read weatherforecasts.write
IdentityServer4.EntityFramework.Stores.ResourceStore: Debug: Found openid, profile, email identity scopes in database
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Requested claim types:
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Information: Profile service returned the following claim types:
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: End userinfo request
您甚至可以在令牌验证期间看到我的自定义专用客户端声明。 但是只有 sub 声明从 identityserver userinfo 端点返回...
我该如何解决这个问题?
编辑
我添加了 ProfileService
,它正在被调用。但正如 Dejan 已经提到的,RequestedClaimTypes
是空的。但是我通过添加
[{"type":"email","identityResourceId":1003},{"type":"sub","identityResourceId":1003}]
记录到 IdentityResourceClaim
table。在输出 window 中,我现在可以看到 email
and sub
claims are requested:
IdentityServer4.Validation.TokenValidator: Debug: Token validation success
{
"ValidateLifetime": true,
"AccessTokenType": "Jwt",
"ExpectedScope": "openid",
"JwtId": "C37AF164BF3A7DE6A28FA7538683248F",
"Claims": {
"nbf": 1637069285,
"exp": 1637155685,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637067659,
"idp": "local",
"email": "pieterjan@example.com",
"name": "Pieterjan",
"jti": "C37AF164BF3A7DE6A28FA7538683248F",
"sid": "BBFA9FD0A06824FA4E982DB1D3669A86",
"iat": 1637069285,
"scope": [
"openid",
"profile",
"email",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": "pwd"
}
}
IdentityServer4.Endpoints.UserInfoEndpoint: Trace: Calling into userinfo response generator: IdentityServer4.ResponseHandling.UserInfoResponseGenerator
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Creating userinfo response
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Scopes in access token: openid profile email weatherforecasts.read weatherforecasts.write
IdentityServer4.EntityFramework.Stores.ResourceStore: Debug: Found openid, profile, email identity scopes in database
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Debug: Requested claim types: email sub
IdentityServer4.ResponseHandling.UserInfoResponseGenerator: Information: Profile service returned the following claim types: email name
IdentityServer4.Endpoints.UserInfoEndpoint: Debug: End userinfo request
IdentityServer4.Hosting.IdentityServerMiddleware: Trace: Invoking result: IdentityServer4.Endpoints.Results.UserInfoResult
代码终于进入了ExternalLoginCallback
,但是await signInManager.GetExternalLoginInfoAsync()
returns null now.
来自https://localhost:44359/connect/userinfo
的回复是
{
"email": "pieterjan@example.com",
"name": "Pieterjan",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8"
}
userinfo 端点从 IdentityServer4.Services.IProfileService
调用 GetProfileDataAsync
以获取请求的声明值。因此,简单的解决方案是实施该服务。
假设您将用户管理器定义为 IMyUserManager
并且它具有此处引用的方法(GetClaimsForUser
和 IsActive
),简化的实现可能如下所示:
public class MyProfileService : IProfileService
{
private readonly IMyUserManager userManager;
public MyProfileService(IMyUserManager usermanager)
{
this.userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// context.RequestedClaimTypes will contain the claims you requested when invoking the token endpoint
var myClaims = await userManager.GetClaimsForUser(context.Subject, context.RequestedClaimTypes);
context.IssuedClaims = myClaims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = await userManager.IsActive(context.Subject);
}
}
然后您需要配置 IdentityServer4 以使用此实现,方法是在启动时调用 AddProfileService
,如下所示:
services.AddIdentityServer()
// ...
.AddProfileService<MyProfileService>();
我还应该补充一点,为了让您想要的声明出现在 context.RequestedClaimTypes
中,它需要与您在调用令牌端点时直接请求的范围相关联。
启动时,在配置 IdentityServer4 时,确保您提供给 AddIdentityResources
调用的身份资源包含所需的声明。