You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure B2C自定义策略中无法读取Salesforce OpenID的custom_attributes声明

问题:Azure B2C自定义策略读取Salesforce的custom_attributes声明失败

场景与现象

  • Salesforce作为身份提供商(IDP),Azure B2C通过OpenID协议对接,其他声明均可正常读取,但无法读取custom_attributes声明。经C#应用验证,Salesforce已正确返回该属性:
    "custom_attributes": {
      "customername": "ABC"
    }
    
  • 在B2C自定义策略的OutputClaims中配置custom_attributes后,登录弹窗提示AADB2C: An exception has occurred.,尝试使用OutputClaimsTransformation仍出现相同异常。
  • 怀疑原因:custom_attributes为JSON对象格式,但Azure B2C的ClaimType无对应数据类型;测试发现若给custom_attributes设置默认值,ClaimTransformation可正常工作。

相关配置

Technical Profile

<TechnicalProfile Id="Salesforce-OpenIdConnect">
  <DisplayName>Salesforce</DisplayName>
  <Protocol Name="OpenIdConnect" />
  <Metadata>
    <Item Key="METADATA">openidcofig meatadata path</Item>
    <Item Key="response_types">code</Item>
    <Item Key="response_mode">form_post</Item>
    <Item Key="scope">openid id profile email</Item>
    <Item Key="issuer">issuer</Item>
    <Item Key="HttpBinding">POST</Item>
    <Item Key="UsePolicyInRedirectUri">0</Item>     
    <!-- Update the Client ID below to the Application ID -->
    <Item Key="client_id">clientid</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="client_secret" StorageReferenceId="B2C_1A_Secret"/>
  </CryptographicKeys>              
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
    <OutputClaim ClaimTypeReferenceId="identityUrl" PartnerClaimType="aud" />
    <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
    <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name" />
    <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
    <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
    <OutputClaim ClaimTypeReferenceId="custom_attributes" />        
    <OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
  </OutputClaims>
  <OutputClaimsTransformations>
    <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
    <OutputClaimsTransformation ReferenceId="GetCustomerNameClaimFromJson" />
    <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
    <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
  </OutputClaimsTransformations>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>

Claim Type

<ClaimType Id="custom_attributes">
  <DisplayName>custom_attributes</DisplayName>
  <DataType>string</DataType>     
  <DefaultPartnerClaimTypes>        
    <Protocol Name="OpenIdConnect" PartnerClaimType="custom_attributes" />         
  </DefaultPartnerClaimTypes>             
</ClaimType>

Claim Transformation

<ClaimsTransformation Id="GetCustomerNameClaimFromJson" TransformationMethod="GetClaimFromJson">
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="custom_attributes" TransformationClaimType="inputJson" />
  </InputClaims>
  <InputParameters>
    <InputParameter Id="claimToExtract" DataType="string" Value="customername" />
  </InputParameters>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="customername" TransformationClaimType="extractedClaim" />
  </OutputClaims>
</ClaimsTransformation>

User Journey

<UserJourney Id="CustomSignUpSignIn">
  <OrchestrationSteps>
    <!-- For social IDP authentication, attempt to find the user account in the directory. -->
    <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
      <ClaimsProviderSelections>
        <ClaimsProviderSelection TargetClaimsExchangeId="SalesforceExchange" />
      </ClaimsProviderSelections>
    </OrchestrationStep>
    <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).  -->
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SalesforceExchange" TechnicalProfileReferenceId="Salesforce-OpenIdConnect" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
  </OrchestrationSteps>
  <ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>

解决方案

方法1:直接提取嵌套字段(推荐)

由于custom_attributes是JSON对象,直接映射会导致B2C无法序列化,因此可以跳过整个对象的映射,直接提取嵌套的customername字段:

  1. 添加customername的ClaimType定义:
<ClaimType Id="customername">
  <DisplayName>Customer Name</DisplayName>
  <DataType>string</DataType>
</ClaimType>
  1. 修改Salesforce-OpenIdConnect技术配置的OutputClaims:

    • 移除<OutputClaim ClaimTypeReferenceId="custom_attributes" />
    • 添加:
      <OutputClaim ClaimTypeReferenceId="customername" PartnerClaimType="custom_attributes.customername" />
      
  2. 移除GetCustomerNameClaimFromJson相关的OutputClaimsTransformation配置,因为不再需要从JSON中提取字段。

方法2:获取完整JSON对象并转换(若需完整对象)

如果必须获取整个custom_attributes对象,需确保B2C能将JSON对象转为字符串:

  1. 保持custom_attributes的ClaimType为string,修改技术配置的Metadata,添加:
<Item Key="ExtractClaimsFromJson">true</Item>

该配置会让B2C将嵌套的JSON对象序列化为字符串后存入Claim。

  1. 保留原有的GetCustomerNameClaimFromJson转换配置,即可从字符串中提取customername。

额外检查

  • 确认Salesforce已将custom_attributes字段加入ID Token的返回范围,必要时调整Salesforce应用的权限配置,确保对应scope已被授权。
  • 启用Azure B2C的Application Insights日志,查看具体异常信息,定位问题根源。

内容的提问来源于stack exchange,提问作者Santa N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 23:48:10