Azure AD B2C Custom Policy – Preventing Email Change After Verification in the Password Reset Flow

Introduction:

his article explains how to hide the “Change Email” (Change Claims) button that appears in the Azure AD B2C forgot password flow when using Display Controls for email verification. We recommend you to read the Azure AD B2C custom policy overview and the Display Controls documentation before reading this article.

When you implement a password reset flow using Display Controls like passwordResetVerificationControl, Azure AD B2C renders a “Change Email” button (also known as “Change Claims”) after the user successfully verifies their email address. While this button is part of the default Display Control behavior, it can create confusion and potentially introduce a security concern in the forgot password scenario.

The Problem: Why the “Change Email” Button Creates Confusion

In the forgot password flow, the user journey follows these steps:

  1. The user enters their email address on the password reset page.
  2. A verification code is sent to the provided email address.
  3. The user enters the verification code to prove ownership of the email.
  4. After successful verification, the user proceeds to set a new password.

After the email verification succeeds in Step 3, Azure AD B2C renders a “Change Claims” button by default on the page. This button allows the user to go back and change the email address they initially provided.

Complex Azure AD b2C forgot password flow custom policy

This creates a problem for the following reasons:

  1. User Confusion: After successfully verifying their email and receiving a success message, users see an unexpected “Change Email” button. This can confuse users into thinking they need to perform an additional action, or that something went wrong with the verification process.
  2. Security Concern: In the password reset flow, the email address is used to look up the user account via the AAD-UserReadUsingEmailAddress-RaiseIfNotExists validation technical profile. If a user changes the email after verification, they could potentially attempt to verify one email address and then switch to a different email address to reset the password for a different account.
  3. Poor User Experience: The presence of the button adds unnecessary complexity to what should be a straightforward password reset experience. Users should verify their email, then proceed to set their new password — without any option to go back and change the verified email.

The passwordResetVerificationControl display control in the extension file is configured as follows:

<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
  <DisplayName>Reset password using email address</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="IpAddressClaimReferenceId">IpAddress</Item>
    <Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
    <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided email address.</Item>
    <Item Key="EnforceEmailVerification">false</Item>
  </Metadata>
  ...
  <DisplayClaims>
    <DisplayClaim DisplayControlReferenceId="passwordResetVerificationControl" />
  </DisplayClaims>
  ...
</TechnicalProfile>

Key points to note:

  1. The ContentDefinitionReferenceId is set to api.localaccountpasswordreset. This is the content definition that we override with a custom page layout to hide the button via CSS.
  2. The EnforceEmailVerification metadata is set to false because the Display Control handles the email verification process itself, rather than relying on the built-in email verification.
  3. The content definition in the extension file overrides the api.localaccountpasswordreset definition with a custom LoadUri pointing to a custom HTML page layout hosted on Azure Blob Storage:
<ContentDefinition Id="api.localaccountpasswordreset">
  <LoadUri>https://sakriish.blob.core.windows.net/krrish/B2C_ForgotPassword.html</LoadUri>
  <RecoveryUri>~/common/default_page_error.html</RecoveryUri>
  <DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.21</DataUri>
  <Metadata>
    <Item Key="DisplayName">Forgot Password</Item>
  </Metadata>
</ContentDefinition>

The DataUri must reference a valid selfasserted page contract version. Version 2.1.21 is used here. Using an invalid version (for example, 2.1.26) will result in an error: “Sorry, but we’re having trouble signing you in. AADB2C: An exception has occurred.”

Changes Done in the Page Layout (B2C_ForgotPassword.html)

The custom HTML page layout hosted on Azure Blob Storage contains CSS rules to hide the “Change Claims” button. Azure AD B2C renders the Display Control button with an ID pattern of {DisplayControlId}_but_change_claims. For the passwordResetVerificationControl, the button ID becomes passwordResetVerificationControl_but_change_claims.

The following CSS rules are added to the custom page layout to hide the button:

/* Hide the change claims button for the password reset verification control */
#passwordResetVerificationControl_but_change_claims {
    display: none !important;
}

/* Hide the change claims CSS class used by Display Controls */
.changeClaims {
    display: none !important;
}

/* Hide ALL change claims buttons regardless of display control prefix */
[id$="_but_change_claims"] {
    display: none !important;
}

The CSS uses three approaches to ensure the button is hidden reliably:

  1. Targeting the specific ID (#passwordResetVerificationControl_but_change_claims): This hides the button for the password reset display control specifically.
  2. Targeting the CSS class (.changeClaims): Azure AD B2C also applies a changeClaims class to the button, which serves as a fallback selector.
  3. Using an attribute ends-with selector ([id$="_but_change_claims"]): This is a wildcard approach that hides any button whose ID ends with _but_change_claims, ensuring it works for all display controls on the page without needing to know the exact display control ID.

The page layout also hides the change email button for other display controls used on other pages (such as the sign-up email verification control):

#emailVerificationControl_but_change_claims {
    display: none !important;
}
#emailVerificationControlCustom_but_change_claims {
    display: none !important;
}
#email_ver_but_edit {
    display: none !important;
}

These additional rules ensure that the “Change Email” button is hidden consistently across all flows that use this custom page layout, including sign-up and forced password reset scenarios.

New Azure AD B2C forgot password flow

Hosting the Custom Page Layout

The custom HTML file must be hosted on a publicly accessible HTTPS endpoint. In this implementation, Azure Blob Storage is used:

  • URL: https://{your_Storage}.blob.core.windows.net/krrish/B2C_ForgotPassword.html

When hosting on Azure Blob Storage, you must ensure:

  1. The blob container allows public access or CORS is configured for https://{your-tenant}.b2clogin.com.
  2. The file is served over HTTPS.
  3. The HTML file follows the Azure AD B2C page layout contract requirements (it must contain a <div id="api"></div> element where B2C injects its content).
Azure storage account

Summary:

By reducing the user experience concern to a targeted solution, we have implemented two changes to hide the “Change Email” button in the Azure AD B2C forgot password flow:

  1. Extension File: Defined the api.localaccountpasswordreset content definition to reference a custom page layout via the LoadUri element, along with a valid DataUri page contract version (selfasserted:2.1.21).
  2. Custom Page Layout (B2C_ForgotPassword.html): Added CSS rules that use a combination of specific ID selectors, class selectors, and attribute selectors to hide the “Change Claims” button rendered by the Display Control.

This approach ensures that after a user verifies their email during the password reset flow, they proceed directly to enter a new password without the confusing “Change Email” option. The solution is purely CSS-based and does not require any changes to the Display Control configuration or the user journey orchestration steps.

Gowtham K

Gowtham K has been awarded as MVP(Most Valuable Professional) for 9 times by Microsoft for his exceptional contribution in Microsoft technologies under the category “Developer Technologies & Security” . He has more than 12 years of experience on Microsoft technologies such as C#, ASP.NET MVC, ASP.NET WEB API, ASP.NET Core, MS SQL Server, Azure, Microsoft Entra ID, Azure AD B2C and other technologies such as JavaScript, jQuery, HTML and CSS .He is also a blogger and author of articles on various technologies. He is also a speaker and delivered talk on various technologies like ASP.NET MVC, Azure and Azure DevOps in the public events.

Leave a Reply

Your email address will not be published. Required fields are marked *