【问题标题】:Azure AD B2C Local Account Password stops working after a few hoursAzure AD B2C 本地帐户密码在几个小时后停止工作
【发布时间】:2017-10-05 15:33:38
【问题描述】:

我根据Active Directory B2C custom policy starter pack for social and local accounts 中的示例为社交和本地帐户创建了自定义策略。我已经启用了 Microsoft 和 Google 的登录并测试了两者都可以正常工作,我还启用了使用本地帐户登录。

我看到的问题是本地帐户。我可以创建一个,密码可以正常工作几个小时(不确定到底多长时间),然后开始给出一个通用的“无效的用户名或密码”。错误。当我为同一用户输入错误的密码时,我会收到一条不同的消息“您的密码不正确”(这对应于相关的日志条目)。

我已启用应用程序洞察,但只能找到以下异常。

任何关于如何清除这两个错误的帮助都会很棒。

""Statebag"": { ""Complex-CLMS"": {}, ""ValidationRequest"": { ""ContentType"": ""Unspecified"", ""Created"": ""2017-10-04T19:17:49.2510644Z"", ""Key"": ""ValidationRequest"", ""Persistent"": true, ""Value"": ""client_id=307&resource=cf87&username=user%domain.com&password=fakep@ss!123&grant_type=password&scope=openid&nca=1;1;login-NonInteractive;False"" }, ""ValidationResponse"": { ""ContentType"": ""Json"", ""Created"": ""2017-10-04T19:17:49.2510644Z"", ""Key"": ""ValidationResponse"", ""Persistent"": true, ""Value"": ""{\""error\"":\""invalid_grant\"",\""error_description\"":\""AADSTS65001: The user or administrator has not consented to use the application with ID '307' named 'IdentityExperienceFramework'. Send an interactive authorization request for this user and resource.\\r\\nTrace ID: 7c4\\r\\nCorrelation ID: 3cc\\r\\nTimestamp: 2017-10-04 19:17:49Z\"",\""error_codes\"":[65001],\""timestamp\"":\""2017-10-04 19:17:49Z\"",\""trace_id\"":\""7c4\"",\""correlation_id\"":\""3cc\""};1;login-NonInteractive;False"" }, ""ComplexItems"": ""_MachineEventQ, REPRM, TCTX, M_EXCP"" } 这是第二个例外

""Key"": ""Exception"", ""Value"": { ""Kind"": ""Handled"", ""HResult"": ""80131500"", ""Message"": ""The technical Profile with id \""AAD-UserWriteUsingLogonEmail\"" in Policy id \""B2C_1A_signup_signin of Tenant id \""xxx.onmicrosoft.com\"" requires that an error be raised if a claims principal record already exists for storing claims. A claims principal of type \""User\"" with identifier claim type id \""signInNames.emailAddress\"" does already exist."", ""Data"": { ""IsPolicySpecificError"": true, ""TenantId"": ""xxx.onmicrosoft.com"", ""PolicyId"": ""B2C_1A_signup_signin"", ""TechnicalProfile.Id"": ""AAD-UserWriteUsingLogonEmail"", ""ClaimsPrincipal.IdentifierClaim.ClaimTypeId"": ""signInNames.emailAddress"", ""ClaimsPrincipal.PrincipalType"": ""User"", ""CreateClaimsPrincipalIfItDoesNotExist"": ""True"", ""RaiseErrorIfClaimsPrincipalAlreadyExists"": ""True"", ""RaiseErrorIfClaimsPrincipalDoesNotExist"": ""False"" } }

这是 TrustFrameworkExtensions.xml 文件的内容。它与示例之间的唯一区别是我使用 2 个提供程序而不是 1 个。

<?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="xxx.onmicrosoft.com" 
  PolicyId="B2C_1A_TrustFrameworkExtensions" 
  PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">

  <BasePolicy>
    <TenantId>xxx.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
  </BasePolicy>
  <BuildingBlocks>

  </BuildingBlocks>

  <ClaimsProviders>

    <ClaimsProvider>
      <DisplayName>Local Account SignIn</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="login-NonInteractive">
          <Metadata>
            <Item Key="client_id">307</Item>
            <Item Key="IdTokenAudience">cf8</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="307" />
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="cf8" />
          </InputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
        <Domain>Employee SignIn with Azure AD</Domain>
        <DisplayName>Employee Login</DisplayName>
        <TechnicalProfiles>
            <TechnicalProfile Id="AzureADProfile">
                <DisplayName>Employee Login</DisplayName>
                <Description>Login with your GP account</Description>
                <Protocol Name="OpenIdConnect"/>
                <OutputTokenFormat>JWT</OutputTokenFormat>
                <Metadata>
                    <Item Key="METADATA">https://login.windows.net/yyy.onmicrosoft.com/.well-known/openid-configuration</Item>
                    <Item Key="ProviderName">https://sts.windows.net/7de/</Item>
                    <Item Key="client_id">f19</Item>
                    <Item Key="IdTokenAudience">f19</Item>
                    <Item Key="response_types">id_token</Item>
                    <Item Key="UsePolicyInRedirectUri">false</Item>
                </Metadata>
                <CryptographicKeys>
                    <Key Id="client_secret" StorageReferenceId="B2C_1A_AzureADAppSecret"/>
                </CryptographicKeys>
                <OutputClaims>
                    <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="oid"/>
                    <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
                    <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
                    <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
                    <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
                    <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="contosoAuthentication" />
                    <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="AzureADContoso" />
                </OutputClaims>
                <OutputClaimsTransformations>
                    <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
                    <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
                    <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
                    <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
                </OutputClaimsTransformations>
                <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
            </TechnicalProfile>
        </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <Domain>google.com</Domain>
      <DisplayName>Google</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="Google-OAUTH">
            <DisplayName>Employee Login</DisplayName>
            <Protocol Name="OAuth2" />
            <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="scope">email</Item>
            <Item Key="HttpBinding">POST</Item>
            <Item Key="UsePolicyInRedirectUri">0</Item>
            <Item Key="client_id">zzz.apps.googleusercontent.com</Item>
            </Metadata>
            <CryptographicKeys>
            <Key Id="client_secret" StorageReferenceId="B2C_1A_GoogleSecret" />
            </CryptographicKeys>
            <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="id" />
            <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
            <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name" />
            <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
            <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="google.com" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
            </OutputClaims>
            <OutputClaimsTransformations>
            <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
            <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
            <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
            <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
            </OutputClaimsTransformations>
            <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
            <ErrorHandlers>
            <ErrorHandler>
                <ErrorResponseFormat>json</ErrorResponseFormat>
                <ResponseMatch>$[?(@@.error == 'invalid_grant')]</ResponseMatch>
                <Action>Reauthenticate</Action>
                <!--In case of authorization code used error, we don't want the user to select his account again.-->
                <!--AdditionalRequestParameters Key="prompt">select_account</AdditionalRequestParameters-->
            </ErrorHandler>
            </ErrorHandlers>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

  </ClaimsProviders>



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

        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>            
            <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
            <ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange" />
            <ClaimsProviderSelection TargetClaimsExchangeId="AzureADExchange" />
          </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="GoogleExchange" TechnicalProfileReferenceId="Google-OAUTH" />
            <ClaimsExchange Id="AzureADExchange" TechnicalProfileReferenceId="AzureADProfile" />
            <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>

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

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

</TrustFrameworkPolicy>

【问题讨论】:

  • 您是否截断了问题中的客户 ID 和受众,或者这些是真正的价值吗? (即 307 和 cf8)
  • 我已经截断了,我始终相信...

标签: azure-ad-b2c


【解决方案1】:

由于实际错误可能是由于目录中的策略和应用程序配置的组合,我将解释为什么需要以这种方式设置这些应用程序,以及可能出现的问题。我希望即使我对根本原因的猜测是错误的,这种解释也能让您进一步调试问题。

考虑一个需要验证用户密码的应用程序。由于应用程序无法访问密码,其中一种方法是使用用户 ID 和密码向 login.microsoftonline.com 发送身份验证请求,并查看是否成功颁发了令牌。

但是,当此应用程序发送请求时,它必须指定某些资源,并且还必须代表用户拥有访问该资源的权限。一个常见的解决方法是让管理员代表所有用户授予权限(即同意)。

IEF 使用相同的模型。您的策略有两个应用程序,一个用作代表用户获取资源令牌的客户端。如果您按照“Custom Policies Get Started Page”上的说明进行操作,则 ProxyIdentityExperienceFramework 是客户端,IdentityExperienceFramework 是资源。根据这些说明,只有 ProxyIdentityExperienceFramework 可以代表用户获取 IdentityExperienceFramework 的令牌,反之亦然。

根据您日志中的错误消息,似乎您可能已经颠倒了客户端和资源的 ID:

用户或管理员未同意使用该应用程序 ID 为“307”的名称为“IdentityExperienceFramework

也就是说,名为IdentityExperienceFramework 的客户端无权获取令牌。

因此,您需要在 Azure AD 门户中检查哪个应用程序有权访问哪个应用程序并更新客户端 ID 和资源 ID。

第二个例外可能真的是设计使然,它只是告诉您在目录中创建的用户已经存在。这通常发生在注册时,并且根据您的政策设置,它通常会向用户呈现一条错误消息,指示该帐户已经存在,他们应该改为登录。

【讨论】:

    【解决方案2】:

    这个错误

    The user or administrator has not consented to use the application with ID '307' named 'IdentityExperienceFramework'.
    

    您是否在 Azure 中的 AZURE AD 刀片中按下了此应用程序上的授予权限按钮

    第二个异常

    您似乎正在尝试再次写入用户主体,并且在您的元数据中它的 RaiseErrorIfUserPrincipalExists 设置为 rue

    奇怪的是,这两者都没有真正帮助我理解为什么你的密码在几个小时后不起作用

    最好的办法是在此处复制政策,我们可以尝试告诉您为什么您尝试写入而不是读取

    【讨论】:

    • 我的策略与示例之间唯一真正的区别在于 trustframeworextensions.xml 文件,我已将其复制到问题中。
    • 我发誓我之前按下了那个授权按钮,但是当我按下它时密码错误消失了,但第二个异常仍然存在。
    • 你能添加技术配置文件 AAD-UserWriteUsingLogonEmail 吗,它在 base 中,虽然在线示例有这行 true 将 true 更改为 false 会阻止记录错误
    • 它设置为 true,如果我将其设为 false,我如何判断该事件是否没有触发其他行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    相关资源
    最近更新 更多