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

Azure AD B2C自定义策略UserInfo端点:移除原声明保留重命名声明

Azure AD B2C自定义策略UserInfo端点声明重命名问题

背景

我正在使用Azure AD B2C自定义策略实现UserInfo端点,已完成UserInfo端点、TechnicalProfile和UserJourney的配置,同时搭建了RESTful ClaimsProvider从数据库获取声明数据。目前UserInfo端点的UserJourney运行正常,RESTful API调用也能正常执行。

问题描述

我需要在UserInfo响应返回前重命名部分声明。在"Token Issuer" ClaimsProvider的TechnicalProfile中,我为signInNames.emailAddress声明添加了PartnerClaimType属性,将其重命名为email:

<!-- 以下声明会在存在于Claims Bag时返回给UserInfo端点 -->
<InputClaims>
  <InputClaim ClaimTypeReferenceId="objectId"/>
  <InputClaim ClaimTypeReferenceId="givenName"/>
  <InputClaim ClaimTypeReferenceId="surname"/>
  <InputClaim ClaimTypeReferenceId="displayName"/>
  <InputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email"/>
</InputClaims>

但响应中同时出现了email和signInNames.emailAddress两个字段,我需要移除signInNames.emailAddress:

{
  "objectId": "fd604948-f5a3-4046-ad08-5c7814839ad0",
  "givenName": "Alice",
  "surname": "Allgood",
  "displayName": "Alice Allgood",
  "email": "******@mydomain.xyz",                    
  "signInNames.emailAddress": "******@mydomain.xyz"  
}

核心问题:如何阻止signInNames.emailAddress出现在UserInfo响应中?

完整配置代码

UserInfo端点的ClaimsProvider

<ClaimsProvider>
  <!-- UserInfo端点的Claims Provider -->
  <DisplayName>Token Issuer</DisplayName>
  <TechnicalProfiles>
    <TechnicalProfile Id="UserInfoIssuer">
      <DisplayName>JSON Issuer</DisplayName>
      <Protocol Name="None"/>
      <OutputTokenFormat>JSON</OutputTokenFormat>
      <CryptographicKeys>
        <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer"/>
      </CryptographicKeys>
      <!-- 以下声明会在存在于Claims Bag时返回给UserInfo端点 -->
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="objectId"/>
        <InputClaim ClaimTypeReferenceId="givenName"/>
        <InputClaim ClaimTypeReferenceId="surname"/>
        <InputClaim ClaimTypeReferenceId="displayName"/>
        <InputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email"/>
      </InputClaims>
    </TechnicalProfile>
    <TechnicalProfile Id="UserInfoAuthorization">
      <DisplayName>UserInfo授权</DisplayName>
      <Protocol Name="None"/>
      <InputTokenFormat>JWT</InputTokenFormat>
      <Metadata>
        <!-- 更新下方的Issuer和Audience -->
        <!-- Audience可选,Issuer必填 -->
        <Item Key="issuer">https://mydomain.b2clogin.com/eb3cdb18-ca7c-4557-8c01-92b5a13725e8/v2.0/</Item>
        <Item Key="audience">[ "1abc4fa4-9aed-4697-af1b-d6310d098a6e" ]</Item>
        <Item Key="client_assertion_type">urn:ietf:params:oauth:client-assertion-type:jwt-bearer</Item>
      </Metadata>
      <CryptographicKeys>
        <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer"/>
      </CryptographicKeys>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
      </OutputClaims>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>

UserInfo端点的UserJourney

<!-- UserInfo端点 -->
<UserJourney Id="UserInfoJourney" DefaultCpimIssuerTechnicalProfileReferenceId="UserInfoIssuer">
  <Authorization>
    <AuthorizationTechnicalProfiles>
      <AuthorizationTechnicalProfile ReferenceId="UserInfoAuthorization"/>
    </AuthorizationTechnicalProfiles>
  </Authorization>
  
  <OrchestrationSteps>
    <OrchestrationStep Order="1" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges UserIdentity="false">
        <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId"/>
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="GetUserInfoClaims" TechnicalProfileReferenceId="UserInfoClaimsViaHttp"/>
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="UserInfoIssuer"/>
  </OrchestrationSteps>
</UserJourney>

包含UserInfo端点配置的RelyingParty

<RelyingParty>
  <DefaultUserJourney ReferenceId="SignIn"/>
  <Endpoints>
    <!-- UserInfo端点 -->
    <Endpoint Id="UserInfo" UserJourneyReferenceId="UserInfoJourney"/>
  </Endpoints>
  
  <TechnicalProfile Id="PolicyProfile">
    <DisplayName>PolicyProfile</DisplayName>
    <Protocol Name="OpenIdConnect"/>
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="displayName"/>
      <OutputClaim ClaimTypeReferenceId="givenName"/>
      <OutputClaim ClaimTypeReferenceId="surname"/>
      <OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email"/>
      <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
      <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}"/>
    </OutputClaims>
    <SubjectNamingInfo ClaimType="sub"/>
  </TechnicalProfile>  
</RelyingParty>

解决方案

方法1:使用声明转换移除原声明

  1. 先定义一个RemoveClaim类型的声明转换:
<ClaimsTransformations>
  <ClaimsTransformation Id="RemoveSignInNamesEmail" TransformationMethod="RemoveClaim">
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="signInNames.emailAddress"/>
    </InputClaims>
  </ClaimsTransformation>
</ClaimsTransformations>
  1. 在UserInfoIssuer TechnicalProfile中添加该转换,放在InputClaims之后:
<TechnicalProfile Id="UserInfoIssuer">
  <!-- 保留原有配置 -->
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="objectId"/>
    <InputClaim ClaimTypeReferenceId="givenName"/>
    <InputClaim ClaimTypeReferenceId="surname"/>
    <InputClaim ClaimTypeReferenceId="displayName"/>
    <InputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email"/>
  </InputClaims>
  <!-- 添加声明转换 -->
  <InputClaimsTransformations>
    <InputClaimsTransformation ReferenceId="RemoveSignInNamesEmail"/>
  </InputClaimsTransformations>
</TechnicalProfile>

方法2:明确控制输出声明(推荐)

将UserInfoIssuer中的InputClaims替换为OutputClaims,仅列出需要返回的声明,未列出的声明不会出现在响应中:

<TechnicalProfile Id="UserInfoIssuer">
  <DisplayName>JSON Issuer</DisplayName>
  <Protocol Name="None"/>
  <OutputTokenFormat>JSON</OutputTokenFormat>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer"/>
  </CryptographicKeys>
  <!-- 替换InputClaims为OutputClaims -->
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="objectId"/>
    <OutputClaim ClaimTypeReferenceId="givenName"/>
    <OutputClaim ClaimTypeReferenceId="surname"/>
    <OutputClaim ClaimTypeReferenceId="displayName"/>
    <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email"/>
    <!-- 其他需要返回的声明请在此处添加 -->
  </OutputClaims>
</TechnicalProfile>

这种方式下,只有OutputClaims中定义的声明会被返回,原signInNames.emailAddress会被映射为email,不会单独出现在响应里。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 19:35:53