非UI交互模式下无法从B2C自定义策略获取login_hint/domain_hint
问题:非UI交互模式下无法获取B2C自定义策略的login_hint/domain_hint参数
通过UI应用捕获邮箱后,向B2C自定义策略发起携带login_hint=xyz@abc.com和domain_hint=abc.com的请求,需要读取这两个参数并根据domain_hint路由对应的OrchestrationStep,但策略内始终无法获取到这两个值,导致Google-OIDC步骤总是被执行。已知使用带UI的api.selfasserted定义时可正常读取查询字符串值,但无UI交互时无法读取。
请求URL示例:
https://mydomain.b2clogin.com/mydomain.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1A_A_A_G_TEST&client_id=xxxxxxxx-aaaa-aaaa-aaaa-xxxxxxxxxx&nonce=defaultNonce&redirect_uri=https%3A%2F%2Fjwt.ms&scope=openid&response_type=id_token&prompt=login&login_hint=test@gmail.com&domain_hint=gmail.com
现有策略的问题分析
- 依赖UI组件的技术Profile:使用
SelfAssertedAttributeProvider作为域名提取的技术Profile,该Provider需要用户交互,非UI模式下无法执行,导致email和domainclaim无法生成。 - 未直接映射domain_hint:未将请求中的
domain_hint参数直接映射到策略内的claim,而是通过login_hint提取域名,既冗余又依赖UI。 - 预条件逻辑错误:当前预条件设置为当
domain等于gmail.com时跳过Google步骤,与期望的"匹配域名时执行该步骤"逻辑相反。
修正后的策略方案
核心修改点
- 在RelyingParty的TechnicalProfile中直接映射
login_hint和domain_hint到对应的claim - 使用无UI的
ClaimsTransformation技术Profile处理域名提取(若需要从login_hint提取域名的场景) - 修正预条件逻辑,确保匹配域名时执行对应的IdP步骤
完整修正后的策略XML
<?xml version="1.0" encoding="utf-8"?> <TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="MyDomain.onmicrosoft.com" PolicyId="B2C_1A_A_A_G_Test" PublicPolicyUri="http://MyDomain.onmicrosoft.com/B2C_1A_A_A_G_Test"> <BasePolicy> <TenantId>MyDomain.onmicrosoft.com</TenantId> <PolicyId>B2C_1A_TrustFrameworkBase</PolicyId> </BasePolicy> <BuildingBlocks> <ClaimsSchema> <ClaimType Id="email"> <DisplayName>Email</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="domain"> <DisplayName>Email Domain</DisplayName> <DataType>string</DataType> </ClaimType> </ClaimsSchema> <ClaimsTransformations> <ClaimsTransformation Id="ExtractDomainFromEmail" TransformationMethod="ParseDomain"> <InputClaims> <InputClaim ClaimTypeReferenceId="email" TransformationClaimType="emailAddress" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="domain" TransformationClaimType="domain" /> </OutputClaims> </ClaimsTransformation> </ClaimsTransformations> </BuildingBlocks> <ClaimsProviders> <!-- 无UI的Claims Transformation Profile --> <ClaimsProvider> <DisplayName>Domain Extractor</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="Extract-Domain-From-Email"> <DisplayName>Extract Domain</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="domain" /> </OutputClaims> <OutputClaimsTransformations> <OutputClaimsTransformation ReferenceId="ExtractDomainFromEmail" /> </OutputClaimsTransformations> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <!-- Google OIDC IdP --> <ClaimsProvider> <Domain>google.com</Domain> <DisplayName>Google</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="Google-OIDC"> <DisplayName>Google</DisplayName> <Protocol Name="OpenIdConnect" /> <Metadata> <Item Key="ProviderName">google</Item> <Item Key="authorization_endpoint">https://accounts.google.com/o/oauth2/auth</Item> <Item Key="AccessTokenEndpoint">https://accounts.google.com/o/oauth2/token</Item> <Item Key="ClaimsEndpoint">https://www.googleapis.com/oauth2/v1/userinfo</Item> <Item Key="client_id">client_id_formygoogle</Item> </Metadata> <CryptographicKeys> <Key Id="client_secret" StorageReferenceId="B2C_1A_GoogleSecret" /> </CryptographicKeys> <OutputClaims> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" /> </OutputClaims> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> </ClaimsProviders> <UserJourneys> <UserJourney Id="SignUpOrSignInaa"> <OrchestrationSteps> <!-- 步骤1:提取域名(可选,若直接用domain_hint可跳过此步骤) --> <OrchestrationStep Order="1" Type="ClaimsExchange"> <Preconditions> <!-- 如果domain_hint已存在,跳过提取步骤 --> <Precondition Type="ClaimExists" ExecuteActionsIf="true"> <Value>domain</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="ExtractDomain" TechnicalProfileReferenceId="Extract-Domain-From-Email" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤2:匹配domain为gmail.com时执行Google登录 --> <OrchestrationStep Order="2" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="false"> <Value>domain</Value> <Value>gmail.com</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="IdP-Google" TechnicalProfileReferenceId="Google-OIDC" /> </ClaimsExchanges> </OrchestrationStep> <!-- 步骤3:颁发令牌 --> <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> <ClientDefinition ReferenceId="DefaultWeb" /> </UserJourney> </UserJourneys> <RelyingParty> <DefaultUserJourney ReferenceId="SignUpOrSignInaa" /> <TechnicalProfile Id="PolicyProfile"> <DisplayName>PolicyProfile</DisplayName> <Protocol Name="OpenIdConnect" /> <InputClaims> <!-- 直接映射login_hint和domain_hint到策略内的claim --> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="login_hint" /> <InputClaim ClaimTypeReferenceId="domain" PartnerClaimType="domain_hint" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="email" /> </OutputClaims> <SubjectNamingInfo ClaimType="email" /> </TechnicalProfile> </RelyingParty> </TrustFrameworkPolicy>
关键说明
- 直接映射参数:在RelyingParty的
InputClaims中添加domain与domain_hint的映射,无需额外提取即可直接获取该参数值。 - 无UI的技术Profile:将域名提取的技术Profile的Handler改为
ClaimsTransformationProtocolProvider,该组件无需用户交互,可在非UI模式下执行。 - 修正预条件:将预条件改为当
domain不等于gmail.com时跳过Google步骤,确保匹配时执行该IdP流程。 - 可选的提取步骤:添加预条件判断,如果
domain已经通过domain_hint传入,则跳过从login_hint提取域名的步骤,提升效率。
内容的提问来源于stack exchange,提问作者Senthil Kumar
相关产品推荐
相关产品推荐

