【问题标题】:Azure AD B2C - Forgot Password User Journey - Don't Allow old password?Azure AD B2C - 忘记密码用户旅程 - 不允许旧密码?
【发布时间】:2021-02-15 12:33:37
【问题描述】:

我正在基于自定义策略构建 Azure AD B2C 配置。登录、个人资料编辑、密码更改等已按需要进行。

但目前我正在努力解决密码忘记政策。我想实现新密码不等于旧密码。谷歌和微软文档总是给我密码更改的例子。当我更改密码时,我必须输入旧密码和新密码。然后我可以比较旧的和新的。比如像here描述的方式

但是当用户忘记了他的密码时,他当然不能输入旧密码来与新密码进行比较。

有没有什么方法可以在不输入旧密码的情况下建立真正的密码忘记策略,但仍确保新密码不等于旧密码?

提前致谢!

亚历克斯

【问题讨论】:

    标签: azure azure-ad-b2c azure-ad-b2c-custom-policy


    【解决方案1】:

    我遇到了同样的问题,Jas Suri 的回答对我帮助很大。但是,我在让它工作时遇到了一些问题。所以,这就是为什么我要分享我的最终解决方案,以防其他人面临同样的问题。

    我使用了以下内容作为基础:https://github.com/azure-ad-b2c/samples/tree/master/policies/password-reset-not-last-password/policy,但做了一些调整。以下是我必须添加到现有政策中才能使其发挥作用的内容:

    ClaimSchema

    这些声明将在以后使用。

        <ClaimsSchema>
          <ClaimType Id="SamePassword">
            <DisplayName>samePassword</DisplayName>
            <DataType>boolean</DataType>
            <UserHelpText />
          </ClaimType>
          <ClaimType Id="resetPasswordObjectId">
            <DisplayName>User's Object ID</DisplayName>
            <DataType>string</DataType>
            <DefaultPartnerClaimTypes>
              <Protocol Name="OAuth2" PartnerClaimType="oid" />
              <Protocol Name="OpenIdConnect" PartnerClaimType="oid" />
              <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" />
            </DefaultPartnerClaimTypes>
            <UserHelpText>Object identifier (ID) of the user object in Azure AD.</UserHelpText>
          </ClaimType>
        </ClaimsSchema>
    

    ClaimsTransformation

    第一个声明转换检查是否设置了 resetPasswordObjectId,如果没有,之前尝试使用新密码登录显然不起作用,因此 newPassword 不一样作为旧/当前密码。第二个声明转换检查声明 SamePassword 是否等于 false。如果不是,则会引发错误并显示“您不能使用旧密码”。稍后会详细介绍。

     <ClaimsTransformations>
      <ClaimsTransformation Id="CheckPasswordEquivalence" TransformationMethod="DoesClaimExist">
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="resetPasswordObjectId" TransformationClaimType="inputClaim" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="SamePassword" TransformationClaimType="outputClaim" />
        </OutputClaims>
      </ClaimsTransformation>
      <ClaimsTransformation Id="AssertSamePasswordIsFalse" TransformationMethod="AssertBooleanClaimIsEqualToValue">
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="SamePassword" TransformationClaimType="inputClaim" />
        </InputClaims>
        <InputParameters>
          <InputParameter Id="valueToCompareTo" DataType="boolean" Value="false" />
        </InputParameters>
      </ClaimsTransformation>
    </ClaimsTransformations>
    

    ClaimsProvider

    总体思路是使用新输入的密码并尝试登录用户。如果登录成功,newPassword 与旧/当前密码相同,不允许使用。登录时,我们创建一个输出声明并将其命名为 resetPasswordObjectId,该声明未设置或等于登录用户的对象 ID。然后我们检查 resetPasswordObjectId 是否存在(在声明转换部分完成),如果不存在,则可以使用 newPassword,因为它与旧/当前密码不同。

    为了在用户输入旧密码的情况下显示正确的错误消息,我们需要在 &lt;LocalizedResources Id="api.localaccountpasswordreset.en"&gt; 中的 TrustFrameworkLocalization.xml 中重写 UserMessageIfClaimsTransformationBooleanValueIsNotEqual,就像这样 &lt;LocalizedString ElementType="ErrorMessage" StringId="UserMessageIfClaimsTransformationBooleanValueIsNotEqual"&gt;You must not use your old password.&lt;/LocalizedString&gt;

    使用下面的声明提供程序时,请确保替换所有标有 TODO 的部分。

       <ClaimsProviders>
    <ClaimsProvider>
      <DisplayName>Password Reset without same password</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="login-NonInteractive-PasswordChange">
          <DisplayName>Local Account SignIn</DisplayName>
          <Protocol Name="OpenIdConnect" />
          <Metadata>
            <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item>
            <Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item>
            <Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item>
            <Item Key="ProviderName">https://sts.windows.net/</Item>
            <!-- TODO  replace YOUR-TENANT-ID -->
            <Item Key="METADATA">https://login.microsoftonline.com/YOUR-TENANT.onmicrosoft.com/.well-known/openid-configuration</Item>
            <!-- TODO replace YOUR-TENANT-ID -->
            <Item Key="authorization_endpoint">https://login.microsoftonline.com/YOUR-TENANT.onmicrosoft.com/oauth2/token</Item>
            <Item Key="response_types">id_token</Item>
            <Item Key="response_mode">query</Item>
            <Item Key="scope">email openid</Item>
            <!-- TODO ensure this line is commented out-->
            <!-- <Item Key="grant_type">password</Item> -->
            <Item Key="UsePolicyInRedirectUri">false</Item>
            <Item Key="HttpBinding">POST</Item>
            <!-- TODO -->
            <!-- ProxyIdentityExperienceFramework application / client id -->
            <Item Key="client_id">YOUR-PROXY-CLIENT-ID</Item>
            <!-- Native App -->
            <!-- TODO -->
            <!-- IdentityExperienceFramework application / client id -->
            <Item Key="IdTokenAudience">YOUR-IDENTITY-CLIENT-ID</Item>
            <!-- Web Api -->
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" />
            <!-- INFO: replaced oldPassword with newPassword, that way we try logging in with the new password. If the login is successful, we know the newPassword is the same as the old / current password-->
            <InputClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" Required="true" />
            <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" />
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
            <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />
            <!-- TODO -->
            <!-- ProxyIdentityExperienceFramework application / client id -->
            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="YOUR-PROXY-CLIENT-ID" />
            <!-- TODO -->
            <!-- IdentityExperienceFramework application / client id -->
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="YOUR-IDENTITY-CLIENT-ID" />
          </InputClaims>
          <OutputClaims>
            <!-- INFO: assign the objectId (oid) to resetPasswordObjectId, since the claim objectId might already be set. In that case there would be no way of knowing whether it was set due to the attempted login with the newPassword-->
            <OutputClaim ClaimTypeReferenceId="resetPasswordObjectId" 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="userPrincipalName" PartnerClaimType="upn" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
          </OutputClaims>
        </TechnicalProfile>
        <!--Logic to check new password is not the same as old password
                        Validates old password before writing new password-->
        <TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
          <DisplayName>Reset password</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
            <!-- set in the TrustFrameworkLocalization.xml  -->
            <!-- <Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">You must not use your old password.</Item> -->
          </Metadata>
          <CryptographicKeys>
            <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
          </CryptographicKeys>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
            <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="login-NonInteractive-PasswordChange" ContinueOnError="true" />
            <ValidationTechnicalProfile ReferenceId="ComparePasswords" />
            <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
              <Preconditions>
                <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                  <Value>SamePassword</Value>
                  <Value>True</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>
          </ValidationTechnicalProfiles>
        </TechnicalProfile>
        <!-- Runs claimsTransformations to make sure new and old passwords differ -->
        <TechnicalProfile Id="ComparePasswords">
          <DisplayName>Compare Email And Verify Email</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="SamePassword" />
          </OutputClaims>
          <OutputClaimsTransformations>
            <OutputClaimsTransformation ReferenceId="CheckPasswordEquivalence" />
            <OutputClaimsTransformation ReferenceId="AssertSamePasswordIsFalse" />
          </OutputClaimsTransformations>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
    

    【讨论】:

      【解决方案2】:

      您可以通过验证技术配置文件的一些逻辑来做到这一点:

      1. 致电login-noninteractivecontinueOnError=true
      2. 如果声明(如 objectId)为 null,则调用 claimTransform 以生成 boolean
      3. 使用boolean 进行处理逻辑,我们称之为pwdIsLastPwd
      4. 调用claimTransform 断言pwdIsLastPwd = false
      5. 如果是true,则使用claimTransform 错误处理程序抛出错误-“您不能使用此密码”
      6. 继续其余的重置密码流程

      参考资料:

      1. https://docs.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile#validationtechnicalprofiles
      2. Call Claim transform from VTP, Boolean ClaimTransform check if claim exists
      3. Assert boolean is true/false
      4. “UserMessageIfClaimsTransformationBooleanValueIsNotEqual 自断言技术配置文件元数据控制技术配置文件呈现给用户的错误消息。”

      【讨论】:

      • 谢谢!这正是我所需要的。现在它正在工作!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-14
      相关资源
      最近更新 更多