服务器和客户端上的声明不匹配
Claims on Server and on Client do not match
使用 IdentityServer 4 v4.0 和 Asp.Net Core Identity 3.1 我在登录后得到声明:
sub: 1
http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Admin
preferred_username: john@domain.com
name: john@domain.com
email: john@domain.com
email_verified: true
amr: pwd
idp: local
auth_time: 1592937212
但是在我用来登录的 OIDC Client JS 中我得到:
sub: "1"
preferred_username: "john@domain.com"
name: "john@domain.com"
email: "john@domain.com"
email_verified: true
amr: ["pwd"] (1)
idp: "local"
auth_time: 1592937212
问题
- 为什么我在 OIDC 客户端上缺少“管理员”角色?
- 为什么
name
声称是电子邮件而不是用户名?
在 OIDC 客户端上,设置为:
const settings : UserManagerSettings = {
automaticSilentRenew: true,
authority: this.environment.authAuthorityUrl,
client_id: 'spa',
filterProtocolClaims: true,
loadUserInfo: true,
post_logout_redirect_uri: this.environment.authPostLogoutRedirectUrl,
redirect_uri: this.environment.authRedirectUrl,
response_mode: 'query',
response_type: 'code',
scope: 'openid profile email offline_access api',
silent_redirect_uri: this.environment.authSilentRedirectUrl
};
在 ASP.NET Core 3.1 Startup 我有:
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(IdentityConfiguration.GetIdentityResources())
.AddInMemoryApiResources(IdentityConfiguration.GetApiResources())
.AddInMemoryApiScopes(IdentityConfiguration.GetApiScopes())
.AddInMemoryClients(IdentityConfiguration.GetClients())
.AddAspNetIdentity<User>();
IdentityConfiguration class 是:
public class IdentityConfiguration {
public static List<ApiResource> GetApiResources() {
return new List<ApiResource> {
new ApiResource("api", "API Resource")
};
}
public static List<ApiScope> GetApiScopes() {
return new List<ApiScope> {
new ApiScope("api", "api")
};
}
public static List<IdentityResource> GetIdentityResources() {
return new List<IdentityResource> {
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
public static List<Client> GetClients(IConfiguration configuration) {
Settings settings = configuration.Get<Settings>();
return new List<Client> {
new Client {
ClientId = "spa",
ClientName = "SPA Client",
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.Code,
AllowOfflineAccess = true,
RequireClientSecret = false,
RequireConsent = false,
RequirePkce = true,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 360,
RefreshTokenUsage = TokenUsage.ReUse,
AlwaysSendClientClaims = true,
UpdateAccessTokenClaimsOnRefresh = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"api"
},
AllowedCorsOrigins = settings.Path.AllowedCorsOrigins,
PostLogoutRedirectUris = settings.Path.PostLogoutRedirectUris,
RedirectUris = settings.Path.RedirectUris
}
};
}
}
更新 1
我使用 UserManager 创建了如下用户:
var user = new User {
// User properties
}
var claims = new List<Claim> {
new Claim(ClaimTypes.Role, "Admin")
}
await userManager.CreateAsync(user, password);
foreach (var claim in claims)
await userManager.AddClaimAsync(user, claim);
我检查了数据库,创建了用户,并在 UserClaims 中保存了声明 table。
更新 2
为了包含用户的全名,我实现了 IdentityService 的 IProfileService:
public class ProfileService : IProfileService {
protected UserManager<User> _userManager;
public ProfileService(UserManager<User> userManager) {
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context) {
User user = await _userManager.GetUserAsync(context.Subject);
List<Claim> claims = new List<Claim> {
new Claim(JwtClaimTypes.Name, user.Name),
};
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context) {
User user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null) && user.IsActive;
}
}
这也做了一些奇怪的事情。服务器声明相同:
sub: 1
http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Admin
preferred_username: john@domain.com
name: john@domain.com
email: john@domain.com
email_verified: true
amr: pwd
idp: local
auth_time: 1592937212
客户声明变为:
sub: 1
name: John Smith
amr: pwd
idp: local
auth_time: 1592937212
因此服务器声明没有更改名称,客户端声明丢失了所有电子邮件和 preferred_username 但获得了正确的名称。
角色一直显示在服务器上,但不显示在客户端上。
- 对于第一个问题,请尝试将
ClaimTypes.Role
替换为 JwtClaimTypes.Role
。
- 关于第二个问题,你的用户名和Email不一样吗?
编辑:
在您的 ProfileService
构造函数中注入 IUserClaimsPrincipalFactory
并将以下更改应用于您的 GetProfileDataAsync()
函数:
private readonly IUserClaimsPrincipalFactory<User> _claimsFactory;
private readonly UserManager<User> _userManager;
public ProfileService(UserManager<User> userManager, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
context.IssuedClaims = claims;
}
使用 IdentityServer 4 v4.0 和 Asp.Net Core Identity 3.1 我在登录后得到声明:
sub: 1
http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Admin
preferred_username: john@domain.com
name: john@domain.com
email: john@domain.com
email_verified: true
amr: pwd
idp: local
auth_time: 1592937212
但是在我用来登录的 OIDC Client JS 中我得到:
sub: "1"
preferred_username: "john@domain.com"
name: "john@domain.com"
email: "john@domain.com"
email_verified: true
amr: ["pwd"] (1)
idp: "local"
auth_time: 1592937212
问题
- 为什么我在 OIDC 客户端上缺少“管理员”角色?
- 为什么
name
声称是电子邮件而不是用户名?
在 OIDC 客户端上,设置为:
const settings : UserManagerSettings = {
automaticSilentRenew: true,
authority: this.environment.authAuthorityUrl,
client_id: 'spa',
filterProtocolClaims: true,
loadUserInfo: true,
post_logout_redirect_uri: this.environment.authPostLogoutRedirectUrl,
redirect_uri: this.environment.authRedirectUrl,
response_mode: 'query',
response_type: 'code',
scope: 'openid profile email offline_access api',
silent_redirect_uri: this.environment.authSilentRedirectUrl
};
在 ASP.NET Core 3.1 Startup 我有:
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(IdentityConfiguration.GetIdentityResources())
.AddInMemoryApiResources(IdentityConfiguration.GetApiResources())
.AddInMemoryApiScopes(IdentityConfiguration.GetApiScopes())
.AddInMemoryClients(IdentityConfiguration.GetClients())
.AddAspNetIdentity<User>();
IdentityConfiguration class 是:
public class IdentityConfiguration {
public static List<ApiResource> GetApiResources() {
return new List<ApiResource> {
new ApiResource("api", "API Resource")
};
}
public static List<ApiScope> GetApiScopes() {
return new List<ApiScope> {
new ApiScope("api", "api")
};
}
public static List<IdentityResource> GetIdentityResources() {
return new List<IdentityResource> {
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
public static List<Client> GetClients(IConfiguration configuration) {
Settings settings = configuration.Get<Settings>();
return new List<Client> {
new Client {
ClientId = "spa",
ClientName = "SPA Client",
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.Code,
AllowOfflineAccess = true,
RequireClientSecret = false,
RequireConsent = false,
RequirePkce = true,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 360,
RefreshTokenUsage = TokenUsage.ReUse,
AlwaysSendClientClaims = true,
UpdateAccessTokenClaimsOnRefresh = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"api"
},
AllowedCorsOrigins = settings.Path.AllowedCorsOrigins,
PostLogoutRedirectUris = settings.Path.PostLogoutRedirectUris,
RedirectUris = settings.Path.RedirectUris
}
};
}
}
更新 1
我使用 UserManager 创建了如下用户:
var user = new User {
// User properties
}
var claims = new List<Claim> {
new Claim(ClaimTypes.Role, "Admin")
}
await userManager.CreateAsync(user, password);
foreach (var claim in claims)
await userManager.AddClaimAsync(user, claim);
我检查了数据库,创建了用户,并在 UserClaims 中保存了声明 table。
更新 2
为了包含用户的全名,我实现了 IdentityService 的 IProfileService:
public class ProfileService : IProfileService {
protected UserManager<User> _userManager;
public ProfileService(UserManager<User> userManager) {
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context) {
User user = await _userManager.GetUserAsync(context.Subject);
List<Claim> claims = new List<Claim> {
new Claim(JwtClaimTypes.Name, user.Name),
};
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context) {
User user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null) && user.IsActive;
}
}
这也做了一些奇怪的事情。服务器声明相同:
sub: 1
http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Admin
preferred_username: john@domain.com
name: john@domain.com
email: john@domain.com
email_verified: true
amr: pwd
idp: local
auth_time: 1592937212
客户声明变为:
sub: 1
name: John Smith
amr: pwd
idp: local
auth_time: 1592937212
因此服务器声明没有更改名称,客户端声明丢失了所有电子邮件和 preferred_username 但获得了正确的名称。
角色一直显示在服务器上,但不显示在客户端上。
- 对于第一个问题,请尝试将
ClaimTypes.Role
替换为JwtClaimTypes.Role
。 - 关于第二个问题,你的用户名和Email不一样吗?
编辑:
在您的 ProfileService
构造函数中注入 IUserClaimsPrincipalFactory
并将以下更改应用于您的 GetProfileDataAsync()
函数:
private readonly IUserClaimsPrincipalFactory<User> _claimsFactory;
private readonly UserManager<User> _userManager;
public ProfileService(UserManager<User> userManager, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
context.IssuedClaims = claims;
}