【问题标题】:TenantId Claim not being returned from IdentityServer4 to Client AppTenantId 声明未从 IdentityServer4 返回到客户端应用程序
【发布时间】:2018-08-07 15:50:30
【问题描述】:

this 问题之后,我最终在用户选择租户后使用HttpContext.SignInAsync(string subject, Claim[] claims) 重载将所选租户ID 作为声明(定义为“TenantId”类型)传递。

然后,我在 this 问题的自定义 AccountChooserResponseGenerator 类中检查此声明,以确定是否需要将用户定向到租户选择器页面,如下所示:

public override async Task<InteractionResponse> ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null)
        {
            var response = await base.ProcessInteractionAsync(request, consent);
            if (response.IsConsent || response.IsLogin || response.IsError)
                return response;

            if (!request.Subject.HasClaim(c=> c.Type == "TenantId" && c.Value != "0"))
                return new InteractionResponse
                {
                    RedirectUrl = "/Tenant"
                };

            return new InteractionResponse();
        }

交互正常,用户在选择租户后被正确重定向回客户端应用程序。

但是,在我的客户端上,我有简单的:

<dl>
            @foreach (var claim in User.Claims)
            {
                <dt>@claim.Type</dt>
                <dd>@claim.Value</dd>
            }
        </dl>

IdentityServer4 快速入门中的 sn-p 以显示声明,遗憾的是,我的 TenantId 声明不存在。

我已在 IdentityServer 设置中的客户端定义中允许它,如下所示:

var client = new Client
                {
... other settings here
                    AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.Email,
                        IdentityServerConstants.StandardScopes.Phone,
                        "TenantId"
                    }
                };

为了让这个 TenantId 声明在我的客户端应用程序中可见,我缺少什么?

编辑:

基于@d_f的cmets,我现在已经将TentantId添加到我服务器的GetIdentityResources()中,如下:

public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email(),
                new IdentityResources.Phone(),
                new IdentityResource("TenantId", new[] {"TenantId"})
            };
        }

我已经编辑了客户的startup.ConfigureServices(IServiceCollection services) 来请求这个额外的范围,如下:

services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
                .AddCookie("Cookies")
                .AddOpenIdConnect("oidc", options =>
                {
                    //other settings not shown

                    options.Scope.Add("TenantId");

                });

并且仍然由指定的 sn-p 在客户端上显示的唯一声明是:

编辑 2:已修复!

最后@RichardGowan 的回答奏效了。那是因为(正如@AdemCaglin 出色地观察到的那样)我使用的是 IdentityServer 的 AspNetIdentity,它有自己的 IProfileService 实现,尽管有所有这些其他设置,但它一直放弃我的自定义 TenantId 声明)。

所以最后,我可以撤消所有其他设置...我没有在 GetIdentityResources 中提及 TenantId 声明,在我的 IdSrv 中的客户端定义中的 AllowedScopes 中没有提及它,并且在我的客户端services.AddAuthentication的配置中没有提到它。

【问题讨论】:

  • 您已为您的客户添加了“TenantId”作为 AllowedScope,但您尚未描述该范围。类似于: services.AddIdentityServer() .AddInMemoryIdentityResources(new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), new IdentityResource("TenantId", new[] {"租户 ID"}) })
  • 并且您必须在客户端配置中请求额外的范围
  • 或简单的.AddInMemoryIdentityResources(GetIdentityResources()) 及更高版本:public static List&lt;IdentityResource&gt; GetIdentityResources() { /*Claims automatically included in OpenId scope*/ var openIdScope = new IdentityResources.OpenId(); openIdScope.UserClaims.Add("TenantId"); /* Available scopes*/ return new List&lt;IdentityResource&gt;{openIdScope, new IdentityResources.Profile(), new IdentityResources.Email(), /*etc*/}; }
  • @d_f 感谢您的反馈...请查看我的编辑说明对我的设置所做的更改...但客户端仍然没有 TenantId 声明?
  • AlwaysIncludeUserClaimsInIdToken 设置添加到您在 IdSrv 中的客户端配置中。

标签: authentication identityserver4


【解决方案1】:

您需要提供并注册IProfileService 的实现,以将您的自定义声明发回给客户:

public class MyProfileService : IProfileService {
    public MyProfileService() {
    }

    public Task GetProfileDataAsync(ProfileDataRequestContext context) {
        // Issue custom claim
        context.IssuedClaims.Add(context.Subject.Claims.First(c => c.Type == 
               "TenantId"));
        return Task.CompletedTask;
    }

    public Task IsActiveAsync(IsActiveContext context) {
        context.IsActive = true;
        return Task.CompletedTask;
    }
}

【讨论】:

  • 没必要。 DefaultProfileService 实现已经完成了这项工作。自定义配置文件服务用于过滤或发布尚未包含在主题中的全新声明。
  • 正如我所提到的,默认的IProfileService 实现也是一样的,因为它内部有context.AddRequestedClaims(context.Subject.Claims);。看来,您执行了一些额外的更改或修复以使其正常工作......
  • @d_f 可能 OP 使用带有身份的身份服务器,并且这个库有自己的 iprofileservice 实现。可以看代码github.com/IdentityServer/IdentityServer4.AspNetIdentity/blob/…
  • @ademcaglin 哇,真是一个绝妙的观察……我也没有意识到这一点!但这正是它。
  • @ademcaglin,我同意,没有看到整个解决方案的可能性太多......这就是为什么我没有推出自己的答案......答案应该是完整的:)跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-28
  • 2020-03-27
  • 2021-05-13
  • 2011-04-18
相关资源
最近更新 更多