如何将来自 IdentityServer 3 的声明存储在 Azure AD B2C 中,或者仅将其包含在 AAD B2C 颁发的令牌中 [英] How to store claims from IdentityServer 3 in Azure AD B2C or just include it in tokens issued by AAD B2C

查看:13
本文介绍了如何将来自 IdentityServer 3 的声明存储在 Azure AD B2C 中,或者仅将其包含在 AAD B2C 颁发的令牌中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以将 Identity Server 3 发出的 oid 声明或基本上任何其他声明传播到 AAD B2C 并使其成为 Azure AD B2C 发出的令牌的一部分?我们需要在客户端拥有一个原始 ID,而我们可以从 sub 和 oid 声明中获得的只是 AAD B2C 用户帐户的 ID.我也想对角色声明做同样的事情.任何帮助将不胜感激.

I'm wondering if it is possible to propagate an oid claim or basically any other claim issued by Identity Server 3 to the AAD B2C and make it be a part of the token issued by Azure AD B2C? We need to have an original ID on the client side and all we can get from sub and oid claims is an ID of an AAD B2C user account. I would also like to do the same with roles claim. Any help would be much appreciated.

这是我的 Identity Server 配置的 TechnicalProfile 在自定义策略 (TrustFrameworkExtensions.xml) 中的样子:

Here is how TechnicalProfile of my Identity Server configuration look like in Custom Policy (TrustFrameworkExtensions.xml):

<TechnicalProfile Id="IdentityServerProfile">
    <DisplayName>IdentityServer</DisplayName>
    <Description>Login with your IdentityServer account</Description>
    <Protocol Name="OpenIdConnect"/>
    <OutputTokenFormat>JWT</OutputTokenFormat>
    <Metadata>
        <Item Key="METADATA">https://{identity_server_hostname}/identity/.well-known/openid-configuration</Item>
        <Item Key="ProviderName">https://{identity_server_hostname}/identity</Item>
        <Item Key="client_id">00000000-0000-0000-0000-000000000000</Item>
        <Item Key="IdTokenAudience">00000000-0000-0000-0000-000000000000</Item>
        <Item Key="response_types">code</Item>
        <Item Key="scope">openid profile customScope</Item>
        <Item Key="UsePolicyInRedirectUri">false</Item>
        <Item Key="AccessTokenResponseFormat">json</Item>
        <Item Key="HttpBinding">POST</Item>
    </Metadata>
    <CryptographicKeys>
        <Key Id="client_secret" StorageReferenceId="B2C_1A_IdentityServerAppSecret"/>
    </CryptographicKeys>
    <OutputClaims>      
        <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="IdentityServer" />
        <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
        <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="tid" />
        <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="sub" />
    </OutputClaims>
    <OutputClaimsTransformations>
        <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
        <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
        <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
    </OutputClaimsTransformations>
    <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>

这是来自 Application Insights 的原始日志,来自具有给定相关 ID 的日志:

And here is the original log from Application Instights from the log with given correlationId:

{  

"TenantId":"{tenant}.onmicrosoft.com","PolicyId":"B2C_1A_signup_signin_capcom","RedirectUri":"http://localhost:3000/",附加参数":{
"client_info":"1","x-client-SKU":"MSAL.JS","x-client-Ver":"0.1.5",客户端请求 ID":a0ef9d95-ba21-4796-ad65-bc9e2b8c5e89"},随机数":bf198ee6-e084-433d-885c-2bd8f0292b3d",状态":7f732141-498e-4445-9efe-9b0d54178ef6","ClientId":"012c0f85-4568-483f-9bca-054d6201ac00","ResponseType":"id_token",响应模式":片段",响应重定向器":{
"URI":"http://localhost:3000/",D":假,WF":真,R":假,S":假},"AppModelVersion":1,ScopedProviders":[

"TenantId":"{tenant}.onmicrosoft.com", "PolicyId":"B2C_1A_signup_signin_capcom", "RedirectUri":"http://localhost:3000/", "AdditionalParameters":{
"client_info":"1", "x-client-SKU":"MSAL.JS", "x-client-Ver":"0.1.5", "client-request-id":"a0ef9d95-ba21-4796-ad65-bc9e2b8c5e89" }, "Nonce":"bf198ee6-e084-433d-885c-2bd8f0292b3d", "State":"7f732141-498e-4445-9efe-9b0d54178ef6", "ClientId":"012c0f85-4568-483f-9bca-054d6201ac00", "ResponseType":"id_token", "ResponseMode":"fragment", "ResponseRedirector":{
"URI":"http://localhost:3000/", "D":false, "WF":true, "R":false, "S":false }, "AppModelVersion":1, "ScopedProviders":[

]}

我注意到并且觉得奇怪的是 ResponseType 值.在我们的 TechnicalProfile 中设置为代码,这里它的值为 id_token.

Something I've noticed and which I found strange is ResponseType value. In our TechnicalProfile is set to code and here its value is id_token.

对 socialIdpUserId 的声明终于通过了.问题所在下面还有一个答案.现在,我仍然有 firstName、lastName 和 displayName 的问题.我遵循与 socialIdpUserId 相同的模式,但我唯一能看到的是默认值.以下是我对它们的配置:

Claim for socialIdpUserId finally passes through. There is also an answer below what the problem was. Now, I still have a problem with having firstName, lastName and displayName. I've followed the same pattern as I did for socialIdpUserId but the only thing I can see are the default values. Here are my configurations for them:

1) 声明类型定义:

<ClaimType Id="displayName">
    <DisplayName>Display Name</DisplayName>
    <DataType>string</DataType>
    <DefaultPartnerClaimTypes>
      <Protocol Name="OAuth2" PartnerClaimType="unique_name" />
      <Protocol Name="OpenIdConnect" PartnerClaimType="displayName" />
      <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
    </DefaultPartnerClaimTypes>
    <UserHelpText>Your display name.</UserHelpText>
    <UserInputType>TextBox</UserInputType>
 </ClaimType>
...
<ClaimType Id="givenName">
    <DisplayName>Given Name</DisplayName>
    <DataType>string</DataType>
    <DefaultPartnerClaimTypes>
      <Protocol Name="OAuth2" PartnerClaimType="given_name" />
      <Protocol Name="OpenIdConnect" PartnerClaimType="given_name" />
      <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" />
    </DefaultPartnerClaimTypes>
    <UserHelpText>Your given name (also known as first name).</UserHelpText>
    <UserInputType>TextBox</UserInputType>
 </ClaimType>
<ClaimType Id="surname">
    <DisplayName>Surname</DisplayName>
    <DataType>string</DataType>
    <DefaultPartnerClaimTypes>
      <Protocol Name="OAuth2" PartnerClaimType="family_name" />
      <Protocol Name="OpenIdConnect" PartnerClaimType="family_name" />
      <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
    </DefaultPartnerClaimTypes>
    <UserHelpText>Your surname (also known as family name or last name).</UserHelpText>
    <UserInputType>TextBox</UserInputType>
 </ClaimType>

并在我的 IdP 的技术配置文件中输出声明:

And output claims in the technical profile for my IdP:

<TechnicalProfiles>
    <TechnicalProfile Id="CapcomProfile">
...
   <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="displayName" />
    <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" DefaultValue="No" />
    <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="lastName" DefaultValue="Name" />
...

最后,RP 输出定义:

And finally, the RP output definition:

<OutputClaims>
  <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName" />
  <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName"  />
  <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="surname" DefaultValue="Not found in IdP" />
...

有什么建议吗?

推荐答案

最后,我完成了这项工作,现在我的大部分声明都通过了.很可能,问题是由于在我的密钥策略 B2C_1A_CapcomIdentityServerAppSecret 中缺少来自 Azure AD 应用程序的适当客户端密码,更具体地说.因此,请仔细检查您是否已将 AAD 应用程序密钥中的相应密钥复制到您的策略密钥.

In the end, I made this work and now most of my claims are passing through. Most likely, the problem was caused by lack of appropriate Client Secret from Azure AD application in my Key Policies, B2C_1A_CapcomIdentityServerAppSecret to be more specific. So, double check it if you have copied over the appropriate one from AAD Application keys to you Policy Keys.

不幸的是,这只是解决方案的一部分,因为它还没有完全工作,而且我没有在客户端收到令牌.但好消息是,在我配置好这个键之后,我开始在 Application Insights 中收到非常有用的异常(您可以在此处查看如何将自定义策略与 Application Insights 联系起来:https://docs.microsoft.com/en-us/azure/active-目录-b2c/active-directory-b2c-troubleshoot-custom).我强烈建议任何对自定义策略有任何问题的人进行设置.此外,如果您像我一样使用 IdentityServer3,请尝试在其一侧临时打开 http 日志记录.示例如下:

Unfortunately, that was just part of the solution as it stll didn't work completely and I didn't recieve token back on the client. But the good thing was, after I had this keys configured well, I started receiving very useful exceptions in Application Insights (you can see here how to connect you Custom Policies with Application Insights: https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-troubleshoot-custom). I highly recommend anyone who has any problem with custom policies to set it up. Also, if you use IdentityServer3 like I do, try with temporary turning on http logging on its side. Here is the example:

LoggingOptions = new LoggingOptions
                {
                    EnableHttpLogging = true
                }

当我在 Application Insights 中分析跟踪时,发生大多数异常是因为我错过了一些属性,例如 socialIdpUserId,它后来在 OutputClaimsTransformations 中使用.如果在将自定义策略上传到 Azure 时立即检查这一点,并收到一些有关错误的详细消息,那就更好了.

While I was analyzing traces in Application Insights, most of the exceptions happened because I missed some of the attributes, for example socialIdpUserId, which is later used in OutputClaimsTransformations. It would be better if this was checked immediately when uploading custom policies to the Azure together with receiving some detailed message about an error.

修复此问题后,我终于开始在客户端接收经过 IdP 身份验证的用户的 userId.现在唯一剩下的是来自配置文件范围的数据,例如 displayName、givenName 和 surname,但我希望我也能很快找到解决方案.作为原始问题的一部分,您可以在 下找到问题描述.

After I have this fixed, I finally started receiving userId of my IdP authenticated user on the client side. The only thing now that has left is data from profile scope such as displayName, givenName and surname but I hope I'll find the solution very soon for that as well. You can find problem description under as a part of the original question.

[更新 - 解决方案]

[UPDATE - SOLUTION]

昨天,在安静地尝试了这么多不同的事情之后,我终于意识到为什么我们没有将所有索赔都归还给客户.它们实际上并不存在于 identity 令牌中,而仅存在于 access 令牌中.AAD B2C 使用第一个,身份令牌,同时执行自定义策略中定义的映射,这就是重点.最后我不得不在 IdentityServer3 端做一些小改动(看看下面的代码).

Yesterday, after quiet a long time spent on trying so many different things, I finally realized why we were not getting all claims back on the client. They actually didn't exist in identity token but only in access token. AAD B2C uses the first one, the identity token, while doing mappings defined in custom policies and that was the whole point. In the end I had to make some small changes on IdentityServer3 side (take a look at the code below).

现在负责发出声明并生成身份和访问令牌的类如下所示:

This is how the class which is responsible for issuing claims and generating both identity and access tokens now looks like:

 public class CustomClaimsProvider : DefaultClaimsProvider
{
    private readonly IIndex<string, IClaimsDefinition> claimDefinitions;

    public CustomClaimsProvider(
        IUserService users,
        IIndex<string, IClaimsDefinition> claimDefinitions)
        : base(users)
    {
        this.claimDefinitions = claimDefinitions;
    }

    public override async Task<IEnumerable<Claim>> GetIdentityTokenClaimsAsync(
       ClaimsPrincipal subject,
       Client client,
       IEnumerable<Scope> scopes,
       bool includeAllIdentityClaims,
       ValidatedRequest request)
    {
        var claims = await base.GetIdentityTokenClaimsAsync(subject, client, scopes, includeAllIdentityClaims, request).ConfigureAwait(false);
        return GetAdditionalClaims(scopes, claims);
    }

    public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(
        ClaimsPrincipal subject,
        Client client,
        IEnumerable<Scope> scopes,
        ValidatedRequest request)
    {
        var claims = await base.GetAccessTokenClaimsAsync(subject, client, scopes, request).ConfigureAwait(false);
        return GetAdditionalClaims(scopes,  claims);
    }

    private IEnumerable<Claim> GetAdditionalClaims(IEnumerable<Scope> scopes, IEnumerable<Claim> claims)
    {
        var scopesList = scopes.ToList();
        var claimsList = claims.ToList();

        foreach (var scope in scopesList.Select(x => x.Name))
        {
            if (claimDefinitions.TryGetValue(scope, out IClaimsDefinition claimDef))
            {
                claimsList.AddRange(claimDef.GetClaims(claims));
            }
        }

        return claimsList;
    }
}

因此,重点是,如果您希望将一些附加声明作为身份令牌的一部分,您还应该覆盖派生自 DefaultClaimsProvider 的类中的 GetIdentityTokenClaimsAsync 方法.

So, the main point is, you should also override GetIdentityTokenClaimsAsync method in the class derived from DefaultClaimsProvider if you want to have some additional claims as a part of your identity token.

这篇关于如何将来自 IdentityServer 3 的声明存储在 Azure AD B2C 中,或者仅将其包含在 AAD B2C 颁发的令牌中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆