Azure AD B2C — Забыли пароль Путешествие пользователя — Не разрешает старый пароль?

#azure #azure-ad-b2c #azure-ad-b2c-custom-policy

#azure #azure-ad-b2c #azure-ad-b2c-пользовательская политика

Вопрос:

Я создаю конфигурацию Azure AD B2C на основе пользовательских политик. Вход в систему, редактирование профиля, смена пароля и т. Д. Уже Работают так, Как требуется.

Но в настоящее время я борюсь с политикой забытых паролей. Я хочу добиться того, чтобы новый пароль не был равен старому. Google и документы Microsoft всегда дают мне примеры смены пароля. Когда я меняю пароль, я должен ввести старый и новый. Тогда я могу сравнить старый и новый. Например, как описано здесь

Но когда пользователь забыл свой пароль, он, конечно, не может ввести старый пароль, чтобы сравнить его с новым.

Есть ли какой-либо способ создать реальную политику забывания пароля без ввода старого пароля, но, тем не менее, убедиться, что новый пароль не совпадает со старым паролем?

Заранее спасибо!

Алекс

Ответ №1:

Вы можете сделать это с некоторой логикой с помощью технических профилей проверки:

  1. Вызов login-noninteractive с continueOnError = true
  2. Вызовите a claimTransform для генерации a boolean , если утверждение (например, ObjectId) null
  3. Используйте boolean для логики продолжения, давайте назовем это pwdIsLastPwd
  4. Вызовите a claimTransform для утверждения pwdIsLastPwd = false
  5. Если это так true , выдайте сообщение об ошибке — «вы не можете использовать этот пароль» с помощью обработчика claimTransform ошибок
  6. Продолжайте с остальной частью процесса сброса пароля

Ссылки:

  1. https://docs.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile#validationtechnicalprofiles
  2. Преобразование запроса на вызов из VTP, Логическое утверждение transform проверяет, существует ли утверждение
  3. Утверждение логического значения равно true / false
  4. «Сообщение пользователя, которое запрашивает преобразование booleanvalueisnotequal, самоутверждающиеся метаданные технического профиля, управляет сообщением об ошибке, которое технический профиль представляет пользователю «.

Комментарии:

1. Спасибо! Это было то, что мне было нужно. Теперь это работает!

Ответ №2:

У меня была такая же проблема, и ответ от Джаса Сури мне очень помог. Однако у меня возникли некоторые проблемы с его запуском. Вот почему я делюсь своим окончательным решением на случай, если кто-то еще столкнется с такой же проблемой.

Я использовал следующее в качестве основы: 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:

Общая идея состоит в том, чтобы использовать недавно введенный пароль и попытаться войти в систему пользователя. В случае успешного входа в систему новый пароль совпадает со старым / текущим паролем и не разрешен. При входе в систему мы создаем требование вывода и называем его resetPasswordObjectId, который либо не задан, либо равен идентификатору объекта вошедшего в систему пользователя. Затем мы проверяем, существует ли resetPasswordObjectId (выполняется в части преобразования утверждений), если нет, можно использовать NewPassword, поскольку он не совпадает со старым / текущим паролем.

Чтобы отобразить правильное сообщение об ошибке в случае, если пользователь ввел старый пароль, нам нужно переопределить UserMessageIfClaimsTransformationBooleanValueIsNotEqual в TrustFrameworkLocalization.xml в <LocalizedResources Id="api.localaccountpasswordreset.en"> таком виде , как этот <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. Я попробовал этот подход, и он сработал для меня, однако вместо отображения сообщения об ошибке для UserMessageIfClaimsTransformationBooleanValueIsNotEqual при использовании текущего пароля в качестве нового пароля он возвращает 500, когда я проверяю консоль в браузере. Сталкивались ли вы с этим, когда внедряли пользовательскую политику выше? Если да, то как вы смогли решить эту проблему?