服务器和客户端上的声明不匹配

时间:2020-06-23 18:57:59

标签: asp.net-core identityserver4 asp.net-core-3.1 asp.net-core-identity oidc-client-js

在使用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

问题

  1. 为什么我在OIDC客户端上缺少角色“管理员”?
  2. 为什么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启动中,我有:

  services
    .AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryPersistedGrants()
    .AddInMemoryIdentityResources(IdentityConfiguration.GetIdentityResources())
    .AddInMemoryApiResources(IdentityConfiguration.GetApiResources())
    .AddInMemoryApiScopes(IdentityConfiguration.GetApiScopes())
    .AddInMemoryClients(IdentityConfiguration.GetClients())
    .AddAspNetIdentity<User>();

IdentityConfiguration类为:

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);

我检查了数据库,创建了User,并将Claim保存在UserClaims表中。

更新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,但会获得正确的名称。

该角色一直显示在服务器上,而不显示在客户端上。

1 个答案:

答案 0 :(得分:1)

  • 对于第一个问题,请尝试将ClaimTypes.Role替换为JwtClaimTypes.Role
  • 关于第二个问题,您的用户名和电子邮件地址不同吗?

编辑:

在您的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;
}