【问题标题】:Unable to query custom REST API within Azure AD B2C custom policy无法在 Azure AD B2C 自定义策略中查询自定义 REST API
【发布时间】:2018-08-23 21:26:08
【问题描述】:

我正在尝试通过创建自定义 Azure AD B2C 自定义策略在新用户注册时调用我们的 REST API。我尝试将 API 调用添加为Integrate REST API claims exchanges in your Azure AD B2C user journey as validation of user input 中所述的验证配置文件,以及Walkthrough: Integrate REST API claims exchanges in your Azure AD B2C user journey as an orchestration step 中所述的用户旅程。

我无法让任何一种情况发挥作用。在这两种情况下,我都会收到以下错误:“AADB2C90027:为 'REST-API-SignUp' 指定的基本凭据无效。请检查凭据是否正确以及资源是否已授予访问权限。 "

当我结合使用 Application Insights 并添加对 API 的调用来验证用户输入时,我看到“抛出了 'Web.TPEngine.Providers.BadArgumentRetryNeededException' 类型的异常。”在其他 Stack Overflow 帖子,我看到 BadArgumentRetryNeededException 与未正确配置的 IdentityExperienceFrameworkProxyIdentityExperienceFramework 应用程序相关联和/或未从一个应用程序授予另一个应用程序的权限。但是,我已经多次验证这些配置正确,并且它们在TrustFrameworkExtensions.xml 文件中被正确引用,所以我认为这不是问题。

我已经测试了在使用和不使用基本身份验证的情况下调用 API,我收到了同样的错误。因此,我还假设问题不是与需要基本身份验证的 API 相关,因为即使 API 需要 no 身份验证,我也会收到此错误。

我将复制下面的一些自定义政策。


来自TrustFrameworkExtensions.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="notrealtenantid.onmicrosoft.com"
  PolicyId="B2C_1A_TrustFrameworkExtensions"
  PublicPolicyUri="http://notrealtenantid.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">

  <BasePolicy>
    <TenantId>notrealtenantid.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
  </BasePolicy>
  <BuildingBlocks>
    <ClaimsSchema>
      <ClaimType Id="loyaltyNumber">
        <DisplayName>loyaltyNumber</DisplayName>
        <DataType>string</DataType>
        <UserHelpText>Customer loyalty number</UserHelpText>
      </ClaimType>
    </ClaimsSchema>
  </BuildingBlocks>
  <ClaimsProviders>

    <!-- Local account claims provider -->
    <ClaimsProvider>
      <DisplayName>Local Account SignIn</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="login-NonInteractive">
          <Metadata>
            <!-- ProxyIdentityExperienceFramework -->
            <Item Key="client_id">732c7aec-0080-4421-bdba-cb0a156b596f</Item>
            <!-- IdentityExperienceFramework -->
            <Item Key="IdTokenAudience">e66b1d02-a473-4a1a-8cc2-b5d22da7d92a</Item>
          </Metadata>
          <InputClaims>
            <!-- ProxyIdentityExperienceFramework -->
            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="732c7aec-0080-4421-bdba-cb0a156b596f" />
            <!-- IdentityExperienceFramework -->
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="e66b1d02-a473-4a1a-8cc2-b5d22da7d92a" />
          </InputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <!-- Facebook claims provider -->
    <ClaimsProvider>
      <DisplayName>Facebook</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="Facebook-OAUTH">
          <Metadata>
            <Item Key="client_id">1234</Item>
            <Item Key="scope">email public_profile</Item>
            <Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
          </Metadata>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>REST APIs</DisplayName>
      <TechnicalProfiles>

        <!-- Custom Restful service -->
        <TechnicalProfile Id="REST-API-SignUp">
          <DisplayName>Validate user's input data and return loyaltyNumber claim</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">https://NotRealUrl.com/1/0/usercheck</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" />
            <InputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
            <InputClaim ClaimTypeReferenceId="surname" PartnerClaimType="lastName" />
            <InputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="loyaltyNumber" PartnerClaimType="loyaltyNumber" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>

  <UserJourneys>
    <UserJourney Id="SignUpOrSignIn">
      <OrchestrationSteps>

        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
            <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
          </ClaimsProviderSelections>
          <ClaimsExchanges>
            <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Check if the user has selected to sign in using one of the social providers -->
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
            <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- For social IDP authentication, attempt to find the user account in the directory. -->
        <OrchestrationStep Order="3" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>authenticationSource</Value>
              <Value>localAccountAuthentication</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId). 
          This can only happen when authentication happened using a social IDP. If local account was created or authentication done
          using ESTS in step 2, then an user account must exist in the directory by this time. -->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent 
          in the token. -->
        <OrchestrationStep Order="5" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>authenticationSource</Value>
              <Value>socialIdpAuthentication</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect 
             from the user. So, in that case, create the user in the directory if one does not already exist 
             (verified using objectId which would be set from the last step if account was created in the directory. -->
        <OrchestrationStep Order="6" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- Do Rest call.
            NOTE!! Sign up works if this orchestration step is not included. -->
        <OrchestrationStep Order="7" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="GetLoyaltyData" TechnicalProfileReferenceId="REST-API-SignUp" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <OrchestrationStep Order="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>

  </UserJourneys>

</TrustFrameworkPolicy>

来自SignUpOrSignin.xml的代码:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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="notrealtenantid.onmicrosoft.com"
  PolicyId="B2C_1A_signup_signin"
  PublicPolicyUri="http://notrealtenantid.onmicrosoft.com/B2C_1A_signup_signin"
  DeploymentMode="Development"
  UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights">

  <BasePolicy>
    <TenantId>notrealtenantid.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>

  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignIn" />
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
        <OutputClaim ClaimTypeReferenceId="identityProvider" />
        <OutputClaim ClaimTypeReferenceId="loyaltyNumber" DefaultValue="" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

如果我注释掉编排步骤 7,这是调用我们的 REST API 的步骤,那么注册过程就会起作用。因此,问题与 API 的调用有关。

有谁知道可能导致“AADB2C90027:为“REST-API-SignUp”指定的基本凭据无效。请检查凭据是否正确以及资源是否已授予访问权限。”错误?我不知所措,因为 API 不需要需要基本身份验证。

【问题讨论】:

标签: azure-ad-b2c


【解决方案1】:

经过一些团队讨论后,我们发现在我们的 Azure 应用程序网关上配置的 Web 应用程序防火墙 (WAF) 阻止了请求并返回 403 的 HTTP 响应,因为 B2C 没有为 User-Agent 发送 HTTP 标头。

该请求在禁用阻止它的 WAF 规则后有效。

创建了一个功能请求,让 B2C 在 https://feedback.azure.com/forums/34192--general-feedback/suggestions/35235046-send-a-user-agent-when-an-azure-ad-b2c-custom-poli 发送 User-Agent 请求标头

与此同时,一种解决方法是禁用需要 User-Agent 标头的 WAF 规则,或者让 B2C 调用不受 WAF 保护的 API。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    相关资源
    最近更新 更多