外部登录需要 nameidentifier 声明才能成功。如何删除令牌中的重复键
nameidentifier claim is required for external login to succeed. How to remove duplicate keys in token
我终于成功地使用 IdentityServer4 设置了一个项目,以允许用户使用一个帐户登录多个应用程序。但是,我觉得不完全是应该的。
这是我的OAuthOptions
class
public class CentralOptions : OAuthOptions
{
public CentralOptions()
{
ClaimsIssuer = "https://localhost:44359";
CallbackPath = new Microsoft.AspNetCore.Http.PathString("/signin-central");
AuthorizationEndpoint = "https://localhost:44359/connect/authorize";
TokenEndpoint = "https://localhost:44359/connect/token";
UserInformationEndpoint = "https://localhost:44359/connect/userinfo";
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
Scope.Add("openid");
Scope.Add("profile");
Scope.Add("email");
Scope.Add("phone");
Scope.Add("role");
Scope.Add("weatherforecasts.read");
Scope.Add("weatherforecasts.write");
UsePkce = true;
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "sub");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "name");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "email");
//ClaimActions.MapJsonKey("sub", "sub");
//ClaimActions.MapJsonKey("name", "name");
//ClaimActions.MapJsonKey("email", "email");
}
}
如您所见,现在我必须复制 RequestedClaims
,一次用于实际声明类型,一次用于某些简称。我一直在调整数据库的内容一段时间,但无法弄清楚我必须更改什么才能只拥有一次声明(我想 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
不应该出现在不记名令牌中,并且email
应该。但是如果我更改数据库和应用程序中的声明,登录将失败,因为它依赖于 the ClaimTypes.NameIdentifier
claim to be present in the bearer token。
我登录后,IS 给了我一个访问令牌,例如:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCOTBDN0JBNkExMjI2RjEyMEU0QzJGOEQzMjIwMzAxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MzcxNzkxNDAsImV4cCI6MTYzNzI2NTU0MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNTkiLCJhdWQiOiJ3ZWF0aGVyZm9yZWNhc3RzIiwiY2xpZW50X2lkIjoiU3NvQXBwbGljYXRpb25DbGllbnQiLCJjZW50cmFsLXRoZWNsaWVudCI6IlRoZSBTU08gY2xpZW50Iiwic3ViIjoiOTU5YzliZmEtZWQzMC00NjM4LTk5ODYtNjNjZjE1ODllZmY4IiwiYXV0aF90aW1lIjoxNjM3MTc5MTM3LCJpZHAiOiJsb2NhbCIsImVtYWlsIjoicGlldGVyamFuQGV4YW1wbGUuY29tIiwibmFtZSI6IlBpZXRlcmphbiIsImlkIjoiOTU5YzliZmEtZWQzMC00NjM4LTk5ODYtNjNjZjE1ODllZmY4IiwicGhvbmUiOiIrMzIxMjMvNDUuNjcuODkiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiUGlldGVyamFuIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzIjoicGlldGVyamFuQGV4YW1wbGUuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbW9iaWxlcGhvbmUiOiIrMzIxMjMvNDUuNjcuODkiLCJqdGkiOiI3RjA0QTA5MDM3MUNEMjQ2MENCQzg3OUY3MDEwOTU1MyIsInNpZCI6IjA0NDYzRDlBRDNENDRCNUExQTNCQTRFOTczRUE5OTI4IiwiaWF0IjoxNjM3MTc5MTQwLCJzY29wZSI6WyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiLCJvcGVuaWQiLCJwcm9maWxlIiwiZW1haWwiLCJwaG9uZSIsInJvbGUiLCJ3ZWF0aGVyZm9yZWNhc3RzLnJlYWQiLCJ3ZWF0aGVyZm9yZWNhc3RzLndyaXRlIl0sImFtciI6WyJwd2QiXX0.KBKLezXnUs6s-bU9hme7Ab7ADZN8DEewqfUncDwR0c2_LFqAnyCw3IZ85VJC4t-NN6xJYu8ROk-cX9PDKIQzEAOWGkOrQuqeaspKfIpl_rCq4qbP7x7uflToqPO245iU6xlzxVnGuaG1o_sSILNQA_YZJV8nsmXJkdB2QonuCZwvrBh5URFXV5cZpivlWznJls9eqfRM9MjlRpWe-NCI6I7FExfCaRgPZ4b1XwyrmmQWNlaKJOmIM3qag1pQshdXBSzg3w65htj89zOKKWSNl6Go6Q_0pZzbv0FLcMUMR_GTzuw56_CFobavD40T65wQQlXxf0cfkzbrdyAx7k8tyg
解码后看起来像这样
{
"alg": "RS256",
"kid": "5B90C7BA6A1226F120E4C2F8D3220301",
"typ": "at+jwt"
}
{
"nbf": 1637179140,
"exp": 1637265540,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637179137,
"idp": "local",
"email": "pieterjan@example.com",
"name": "Pieterjan",
"id": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"phone": "+32123/45.67.89",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Pieterjan",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "pieterjan@example.com",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone": "+32123/45.67.89",
"jti": "7F04A090371CD2460CBC879F70109553",
"sid": "04463D9AD3D44B5A1A3BA4E973EA9928",
"iat": 1637179140,
"scope": [
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"openid",
"profile",
"email",
"phone",
"role",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": [
"pwd"
]
}
使用此令牌,您可以向
发送请求
https://localhost:44359/connect/userinfo
这给出了以下响应
{
"email": "pieterjan@example.com",
"name": "Pieterjan",
"id": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"phone": "+32123/45.67.89",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Pieterjan",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "pieterjan@example.com",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone": "+32123/45.67.89",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8"
}
在我看来,您应该在响应中只有短名称限定符(email
、name
、sub
、phone
),是吗正确的?但是如果我重新排列这个,/connect/userinfo
的响应将不包含 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
声明,并且登录将在 following line 失败,它会在这个 UserInfo 的响应中查找这个确切的声明端点,因此将失败。
我想我必须调整 OAuthOptions.ClaimActions
当前阅读的内容:
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "sub");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "name");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "email");
我已经在研究它们了,但最细微的修改会导致所有声明不再从 IdentityServer 返回。
dbo.Clients
dbo.ClientGrantType
= client.allowedGrantTypes
dbo.ClientSecret
= client.ClientSecrets
dbo.ClientScope
= client.AllowedScopes
所以在这里你看到我现在有可能不应该在数据库中的范围,因为它们实际上是声明类型,但如果我删除它们,NameIdentifier
声明类型将不存在在身份中。
dbo.ClientRedirectUri
= client.RedirectUris
dbo.ClientClaim
= client.Claims
dbo.IdentityResources
dbo.IdentityResourceClaim
= identityResource.UserClaims
其中标记为:
List of associated user claims that should be included when this resource is requested.
很明显,我必须引入双线才能让外部登录开始工作。
dbo.AspNetUsers
dbo.AspNetUserClaims
= user.Claims
如何正确设置我的代码和数据库,以便我的应用程序不再需要那些重复的声明,work/my 外部登录才能成功?
此外,声明应该在数据库级别持久化,还是 generated during login?
提前致谢。
我只会在 IdentityServer 中使用较短的声明名称,并在客户端中进行必要的声明转换或映射。
我会考虑在客户端或 API 使用 MapUniqueJsonKey 进行转换:
options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");
我认为了解 ClaimsPrincipal 用户对象在身份验证后(但在授权前)包含的内容很重要。
如果需要更高级的转换,请查看使用 IClaimsTransformation 接口。
更多信息:
我终于成功地使用 IdentityServer4 设置了一个项目,以允许用户使用一个帐户登录多个应用程序。但是,我觉得不完全是应该的。
这是我的OAuthOptions
class
public class CentralOptions : OAuthOptions
{
public CentralOptions()
{
ClaimsIssuer = "https://localhost:44359";
CallbackPath = new Microsoft.AspNetCore.Http.PathString("/signin-central");
AuthorizationEndpoint = "https://localhost:44359/connect/authorize";
TokenEndpoint = "https://localhost:44359/connect/token";
UserInformationEndpoint = "https://localhost:44359/connect/userinfo";
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
Scope.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
Scope.Add("openid");
Scope.Add("profile");
Scope.Add("email");
Scope.Add("phone");
Scope.Add("role");
Scope.Add("weatherforecasts.read");
Scope.Add("weatherforecasts.write");
UsePkce = true;
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "sub");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "name");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "email");
//ClaimActions.MapJsonKey("sub", "sub");
//ClaimActions.MapJsonKey("name", "name");
//ClaimActions.MapJsonKey("email", "email");
}
}
如您所见,现在我必须复制 RequestedClaims
,一次用于实际声明类型,一次用于某些简称。我一直在调整数据库的内容一段时间,但无法弄清楚我必须更改什么才能只拥有一次声明(我想 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
不应该出现在不记名令牌中,并且email
应该。但是如果我更改数据库和应用程序中的声明,登录将失败,因为它依赖于 the ClaimTypes.NameIdentifier
claim to be present in the bearer token。
我登录后,IS 给了我一个访问令牌,例如:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCOTBDN0JBNkExMjI2RjEyMEU0QzJGOEQzMjIwMzAxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MzcxNzkxNDAsImV4cCI6MTYzNzI2NTU0MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNTkiLCJhdWQiOiJ3ZWF0aGVyZm9yZWNhc3RzIiwiY2xpZW50X2lkIjoiU3NvQXBwbGljYXRpb25DbGllbnQiLCJjZW50cmFsLXRoZWNsaWVudCI6IlRoZSBTU08gY2xpZW50Iiwic3ViIjoiOTU5YzliZmEtZWQzMC00NjM4LTk5ODYtNjNjZjE1ODllZmY4IiwiYXV0aF90aW1lIjoxNjM3MTc5MTM3LCJpZHAiOiJsb2NhbCIsImVtYWlsIjoicGlldGVyamFuQGV4YW1wbGUuY29tIiwibmFtZSI6IlBpZXRlcmphbiIsImlkIjoiOTU5YzliZmEtZWQzMC00NjM4LTk5ODYtNjNjZjE1ODllZmY4IiwicGhvbmUiOiIrMzIxMjMvNDUuNjcuODkiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiUGlldGVyamFuIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzIjoicGlldGVyamFuQGV4YW1wbGUuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbW9iaWxlcGhvbmUiOiIrMzIxMjMvNDUuNjcuODkiLCJqdGkiOiI3RjA0QTA5MDM3MUNEMjQ2MENCQzg3OUY3MDEwOTU1MyIsInNpZCI6IjA0NDYzRDlBRDNENDRCNUExQTNCQTRFOTczRUE5OTI4IiwiaWF0IjoxNjM3MTc5MTQwLCJzY29wZSI6WyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiLCJvcGVuaWQiLCJwcm9maWxlIiwiZW1haWwiLCJwaG9uZSIsInJvbGUiLCJ3ZWF0aGVyZm9yZWNhc3RzLnJlYWQiLCJ3ZWF0aGVyZm9yZWNhc3RzLndyaXRlIl0sImFtciI6WyJwd2QiXX0.KBKLezXnUs6s-bU9hme7Ab7ADZN8DEewqfUncDwR0c2_LFqAnyCw3IZ85VJC4t-NN6xJYu8ROk-cX9PDKIQzEAOWGkOrQuqeaspKfIpl_rCq4qbP7x7uflToqPO245iU6xlzxVnGuaG1o_sSILNQA_YZJV8nsmXJkdB2QonuCZwvrBh5URFXV5cZpivlWznJls9eqfRM9MjlRpWe-NCI6I7FExfCaRgPZ4b1XwyrmmQWNlaKJOmIM3qag1pQshdXBSzg3w65htj89zOKKWSNl6Go6Q_0pZzbv0FLcMUMR_GTzuw56_CFobavD40T65wQQlXxf0cfkzbrdyAx7k8tyg
解码后看起来像这样
{
"alg": "RS256",
"kid": "5B90C7BA6A1226F120E4C2F8D3220301",
"typ": "at+jwt"
}
{
"nbf": 1637179140,
"exp": 1637265540,
"iss": "https://localhost:44359",
"aud": "weatherforecasts",
"client_id": "SsoApplicationClient",
"central-theclient": "The SSO client",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"auth_time": 1637179137,
"idp": "local",
"email": "pieterjan@example.com",
"name": "Pieterjan",
"id": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"phone": "+32123/45.67.89",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Pieterjan",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "pieterjan@example.com",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone": "+32123/45.67.89",
"jti": "7F04A090371CD2460CBC879F70109553",
"sid": "04463D9AD3D44B5A1A3BA4E973EA9928",
"iat": 1637179140,
"scope": [
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"openid",
"profile",
"email",
"phone",
"role",
"weatherforecasts.read",
"weatherforecasts.write"
],
"amr": [
"pwd"
]
}
使用此令牌,您可以向
发送请求https://localhost:44359/connect/userinfo
这给出了以下响应
{
"email": "pieterjan@example.com",
"name": "Pieterjan",
"id": "959c9bfa-ed30-4638-9986-63cf1589eff8",
"phone": "+32123/45.67.89",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Pieterjan",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "pieterjan@example.com",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone": "+32123/45.67.89",
"sub": "959c9bfa-ed30-4638-9986-63cf1589eff8"
}
在我看来,您应该在响应中只有短名称限定符(email
、name
、sub
、phone
),是吗正确的?但是如果我重新排列这个,/connect/userinfo
的响应将不包含 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
声明,并且登录将在 following line 失败,它会在这个 UserInfo 的响应中查找这个确切的声明端点,因此将失败。
我想我必须调整 OAuthOptions.ClaimActions
当前阅读的内容:
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "sub");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "name");
ClaimActions.MapJsonKey("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "email");
我已经在研究它们了,但最细微的修改会导致所有声明不再从 IdentityServer 返回。
dbo.Clients
dbo.ClientGrantType
= client.allowedGrantTypes
dbo.ClientSecret
= client.ClientSecrets
dbo.ClientScope
= client.AllowedScopes
所以在这里你看到我现在有可能不应该在数据库中的范围,因为它们实际上是声明类型,但如果我删除它们,NameIdentifier
声明类型将不存在在身份中。
dbo.ClientRedirectUri
= client.RedirectUris
dbo.ClientClaim
= client.Claims
dbo.IdentityResources
dbo.IdentityResourceClaim
= identityResource.UserClaims
其中标记为:
List of associated user claims that should be included when this resource is requested.
很明显,我必须引入双线才能让外部登录开始工作。
dbo.AspNetUsers
dbo.AspNetUserClaims
= user.Claims
如何正确设置我的代码和数据库,以便我的应用程序不再需要那些重复的声明,work/my 外部登录才能成功?
此外,声明应该在数据库级别持久化,还是 generated during login?
提前致谢。
我只会在 IdentityServer 中使用较短的声明名称,并在客户端中进行必要的声明转换或映射。
我会考虑在客户端或 API 使用 MapUniqueJsonKey 进行转换:
options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");
我认为了解 ClaimsPrincipal 用户对象在身份验证后(但在授权前)包含的内容很重要。
如果需要更高级的转换,请查看使用 IClaimsTransformation 接口。
更多信息: