15

I am using Azure B2C to handle user access to my App. I have an issue with the reset password workflow.

As part of the reset password process a verification email is sent to the registered email address. However the default template for this is generic and does not look professional or in any way linked to my App.

Is there anyway of customising this template? I have already customised the B2C html pages but cannot find a template for the email?

Thanks Ash

2

3 Answers 3

13

Azure AD B2C supports basic customization of emails sent by Azure AD B2C through Azure AD's Company Branding feature. Customization of the template is not supported at this time, though you can vote for that ask in the Azure AD B2C Feedback forum: Fully customizable verification emails

An alternative would be for you to avoid using the Reset Password policy at all and implement your own reset password mechanism which:

  1. Generates its own codes
  2. Sends its own emails
  3. Has its own code redemption mechanism
  4. Uses the Graph API to reset the user's password
Sign up to request clarification or add additional context in comments.

2 Comments

Hey @Saca... could you please shed some light here: stackoverflow.com/q/58646052/114029
The Azure AD Company Branding feature had no effect on the password reset verification code email for me. Anyone know if it should work as described above?
7

Azure has update it. Now, it is available here. https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-email

1 Comment

That's for custom policies only. Not for default userflows.
0

MSFT now allows you to use your own service as API. You can still use B2C to generate and verify the code, but you can hook your own service to send the email with branding.

Unfortunately, there's not much documentation around this. Some articles are around 7 years ago which are not recommended as it lacks security. I have written an article on this with detailed changes to the base policies.

Make below changes to your extension policy

Add Claims

<ClaimType Id="verificationCode">
  <DisplayName>Verification Code</DisplayName>
  <DataType>string</DataType>
  <UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="otpGenerated">
  <DisplayName>Generated OTP</DisplayName>
  <DataType>string</DataType>
</ClaimType>

Display controls need 2.1.9 or higher on the self‑asserted content definition used by your reset page

<ContentDefinition Id="api.localaccountpasswordreset">
  <LoadUri>https://YOUR-STATIC-WEB-APP/reset-password.html</LoadUri>
  <RecoveryUri>~/common/default_page_error.html</RecoveryUri>
  <DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.9</DataUri>
</ContentDefinition>

Define the Verification display control

Still in TrustFrameworkExtensions.xml, add under <BuildingBlocks>

<DisplayControls>
  <DisplayControl Id="emailVerificationControl" UserInterfaceControlType="VerificationControl">
    <DisplayClaims>
      <DisplayClaim ClaimTypeReferenceId="email" Required="true" />
      <DisplayClaim ClaimTypeReferenceId="verificationCode"
                    ControlClaimType="VerificationCode"
                    Required="true" />
    </DisplayClaims>
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="email" />
    </OutputClaims>
    <Actions>
      <Action Id="SendCode">
        <ValidationClaimsExchange>
          <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="OTP-GenerateCode" />
          <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="REST-SendVerificationEmail" />
        </ValidationClaimsExchange>
      </Action>
      <Action Id="VerifyCode">
        <ValidationClaimsExchange>
          <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="OTP-VerifyCode" />
        </ValidationClaimsExchange>
      </Action>
    </Actions>
  </DisplayControl>

OTP generate & verify

Add a new ClaimsProvider with two technical profiles:

<ClaimsProvider>
  <DisplayName>One-Time Password</DisplayName>
  <TechnicalProfiles>

    <TechnicalProfile Id="OTP-GenerateCode">
      <DisplayName>Generate Code</DisplayName>
      <Protocol Name="Proprietary"
                Handler="Web.TPEngine.Providers.OneTimePasswordProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="Operation">GenerateCode</Item>
        <Item Key="CodeExpirationInSeconds">600</Item>
        <Item Key="CodeLength">6</Item>
        <Item Key="CharacterSet">0-9</Item>
        <Item Key="NumRetryAttempts">5</Item>
        <Item Key="ReuseSameCode">false</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="identifier" />
      </InputClaims>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="otpGenerated" PartnerClaimType="otpGenerated" />
      </OutputClaims>
    </TechnicalProfile>

    <TechnicalProfile Id="OTP-VerifyCode">
      <DisplayName>Verify Code</DisplayName>
      <Protocol Name="Proprietary"
                Handler="Web.TPEngine.Providers.OneTimePasswordProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="Operation">VerifyCode</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="identifier" />
        <InputClaim ClaimTypeReferenceId="verificationCode" PartnerClaimType="otpToVerify" />
      </InputClaims>
    </TechnicalProfile>

  </TechnicalProfiles>
</ClaimsProvider>

REST email sender TP

Add a REST TP that calls your backend to send the code:

<ClaimsProvider>
  <DisplayName>Custom Email Provider</DisplayName>
  <TechnicalProfiles>
    <TechnicalProfile Id="REST-SendVerificationEmail">
      <DisplayName>Send Verification Email</DisplayName>
      <Protocol Name="Proprietary"
                Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="ServiceUrl">https://YOUR-FUNCTION.azurewebsites.net/api/SendVerificationEmail</Item>
        <Item Key="SendClaimsIn">Body</Item>
        <Item Key="AuthenticationType">None</Item>
        <Item Key="AllowInsecureAuthInProduction">false</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="to" />
        <InputClaim ClaimTypeReferenceId="otpGenerated" PartnerClaimType="code" />
      </InputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>

Override the discovery TP to use the display control

The reset journey usually calls LocalAccountDiscoveryUsingEmailAddress at step 1. Override it (same Id) and attach the display control via DisplayClaims (note: not DisplayControlReferences—that’s invalid XML for B2C TPs)

<ClaimsProvider>
  <DisplayName>Local Account</DisplayName>
  <TechnicalProfiles>
    <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="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
      </Metadata>
      <IncludeInSso>false</IncludeInSso>
      <!-- Hook up the display control -->
      <DisplayClaims>
        <DisplayClaim DisplayControlReferenceId="emailVerificationControl" />
      </DisplayClaims>
      <!-- After verification, also ensure the account exists -->
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
        <OutputClaim ClaimTypeReferenceId="objectId" />
        <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
        <OutputClaim ClaimTypeReferenceId="authenticationSource" />
      </OutputClaims>
      <ValidationTechnicalProfiles>
        <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
      </ValidationTechnicalProfiles>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>

Write a simple Azure Function / endpoint to your API which accepts below contract. You can implement whatever email service you want to use. Just return simple 200, as your policy doesn't required any output claim from your rest api

{ "to": "[email protected]", "code": "123456" }

https://medium.com/@jauraamit/b2cazure-ad-b2c-custom-password-reset-email-with-verificationcontrol-otp-1ef0157936c1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.