繁体   English   中英

Azure AD B2C - 忘记密码用户之旅 - 不允许使用旧密码?

[英]Azure AD B2C - Forgot Password User Journey - Don't Allow old password?

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

但目前我正在为密码忘记政策而苦苦挣扎。 我想实现新密码不等于旧密码。 Google 和 Microsoft 文档总是给我一些更改密码的示例。 当我更改密码时,我必须输入旧密码和新密码。 然后我就可以比较旧的和新的了。 例如像这里描述的方式

但是,当用户忘记密码时,他当然无法输入旧密码与新密码进行比较。

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

提前致谢!

亚历克斯

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

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

声明架构

稍后将使用这些声明。

    <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>

声明转换

第一个声明转换检查是否设置了resetPasswordObjectId ,如果未设置,则先前尝试使用新密码登录显然无效,因此新密码与旧/当前密码不同。 第二个声明转换检查声明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>

索赔提供者

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

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

使用下面的索赔提供者时,请确保更换所有标有 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>

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

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

参考:

  1. https://docs.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile#validationtechnicalprofiles
  2. 从 VTP 调用声明转换Boolean ClaimTransform 检查声明是否存在
  3. 断言 boolean 为真/假
  4. “UserMessageIfClaimsTransformationBooleanValueIsNotEqual 自断言技术配置文件元数据控制技术配置文件呈现给用户的错误消息。”

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM