如何将源Entra ID的真实用户UPN通过JWT声明传递给B2C联合应用
解决AAD B2C自定义策略中传递源Entra ID租户UPN到客户端JWT的问题
问题背景
我们有一个面向客户的应用要迁移到AAD B2C实现CIAM,同时支持员工用企业Entra ID登录,采用自定义策略部署。目前已完成基础联合配置,但客户端JWT中拿到的userPrincipalName是B2C生成的cpim_...@mytenant.onmicrosoft.com格式,而非源Entra ID租户的真实UPN,且我们不需要在B2C中存储该值,仅需动态传递。
已做过的操作:
- 按官方文档完成Entra ID与B2C的联合配置
- 在源Entra ID应用注册的「令牌配置」中添加了
upn作为ID令牌的附加声明 - 在Entra ID联合技术配置文件和RP文件中添加了
<OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn"/>,尝试过PartnerClaimType="preferred_username"也无效
解决方案
核心原因是B2C的默认userPrincipalName字段会被系统自动覆盖为自身生成的cpim格式值,所以需要用自定义声明字段先捕获源UPN,再映射到客户端需要的字段:
- 定义自定义声明类型
在扩展策略的<BuildingBlocks>下的<ClaimsSchema>中添加自定义声明,用于存储源租户UPN:
<ClaimType Id="sourceTenantUpn"> <DisplayName>Source Entra ID UPN</DisplayName> <DataType>string</DataType> </ClaimType>
- 修改Entra ID联合技术配置文件
找到你配置的Entra ID联合技术配置文件(比如基于OIDC-Common的技术配置文件),在<InputClaims>中添加捕获源UPN的配置,同时在<OutputClaims>中输出该自定义声明:
<TechnicalProfile Id="AAD-Enterprise"> <DisplayName>Enterprise Entra ID Login</DisplayName> <Protocol Name="OpenIdConnect"/> <!-- 其他配置省略 --> <InputClaims> <!-- 捕获源Entra ID传来的upn到自定义声明 --> <InputClaim ClaimTypeReferenceId="sourceTenantUpn" PartnerClaimType="upn" /> </InputClaims> <OutputClaims> <!-- 输出自定义声明,避免被B2C系统字段覆盖 --> <OutputClaim ClaimTypeReferenceId="sourceTenantUpn" /> <!-- 其他原有输出声明保留 --> </OutputClaims> <!-- 其他配置省略 --> </TechnicalProfile>
- 在信赖方(RP)文件中映射到客户端需要的字段
在RP策略的<OutputClaims>中,将自定义声明映射为客户端期望的userPrincipalName字段:
<RelyingParty> <DefaultUserJourney ReferenceId="SignUpOrSignIn"/> <TechnicalProfile Id="PolicyProfile"> <DisplayName>PolicyProfile</DisplayName> <Protocol Name="OpenIdConnect"/> <OutputClaims> <!-- 用自定义声明覆盖默认的userPrincipalName --> <OutputClaim ClaimTypeReferenceId="sourceTenantUpn" PartnerClaimType="userPrincipalName" /> <!-- 其他需要返回给客户端的声明 --> </OutputClaims> <!-- 其他配置省略 --> </TechnicalProfile> </RelyingParty>
- 验证源Entra ID的令牌是否包含UPN
解码B2C从源Entra ID获取的ID令牌,确认upn字段存在且值正确。如果不存在,需要回到源Entra ID的应用注册,检查「令牌配置」中的附加声明是否正确添加,且确保应用注册的权限配置无误。
关键说明
不要直接使用B2C的默认userPrincipalName字段传递源UPN,因为B2C在身份验证流程中会自动生成并覆盖该字段为自身的cpim格式值。通过自定义声明先捕获源UPN,再在RP中映射到客户端所需的字段名,就能避免被覆盖的问题。
内容的提问来源于stack exchange,提问作者vsqz79
相关产品推荐
相关产品推荐

