In today’s fast-paced digital world, user experience is paramount. One of the common challenges faced by organizations using Azure Active Directory B2C for authentication is the default password reset flow. The process, though functional, lacks personal touch and branding consistency. Frederic, one of our integration experts, created a guide for you on how to implement a custom password reset flow using Azure B2C and Logic App.
Default vs. Desired: Rethinking B2C Password Reset Flows with Logic App
The existing user experience involves clicking on a ‘Forgot Password’ button, entering an email address, and receiving a minimalist email from a Microsoft account with the subject ‘Microsoft on behalf of the app tenant’. This email is very minimalistic and frankly lacks personal branding. In this email, you can find and copy a 6-digit code to verify that they have access to the inbox. Then you can reset the password. While functional, it lacks some convenience.
Frederic envisioned a seamless process where a user, after clicking ‘Forgot Password’, receives a fully customized email from your organization-specific address. This email contains a ‘magic link’ that, when clicked, allows the user to reset the password. The link, ensuring inbox access, expires after a 60-minute window. I’ll showcase an user-friendly password reset flow for Azure B2C. The complete source code can be found in my GitHub repository.
Our blueprint for the best solution
Your Azure B2C tenant needs to be set up to use custom policies. Detailed instructions can be found here. Additionally, create an App registration for Logic App with a redirect URL to https://jwt.ms, allowing for the issuance of Access tokens. You’ll need the ClientID later in the Logic App action that retrieves an user-id token.
Frederic’s solution involves three custom policies within the B2C tenant, paving the way for a more personalized and efficient password reset process. Those custom policies are the following:
- SignUpOrSignIn Custom Policy
This derives from the standard starter pack policy. This overrides the ‘Forgot Password’ link button with a sub journey, prompting the user to input their email address and triggering a REST API call to a Logic App for email dispatch.
- Generate Password Token Custom Policy
A policy with no User Interface gets triggered from an action in the LogicApp. It creates a token comprising the email address, an expiry time, and a signature.
- Reset Password Custom Policy
It first confirms the validity of the id_token and then displays a new form. The user sees their email in a read-only format and is prompted to enter and confirm their new password.
Now let’s dive into some relevant details of the different policies.
SignUpOrSignIn ContentDefinition that overwrites the ‘Forgot Password’ link:

The ForgotPasswordExchange is a ClaimsTransformation with a TechnicalProfileReferenceId named “ForgotPassword” that looks like this:

SignUpOrSignIn Technical Profile to make the REST API call to the LogicApp.

The GeneratePasswordToken policy has a UserJourney with only one orchestration step that invokes the TechnicalProfile to issue a JWT token. Here you can define how long the magic link should be valid (like for example those 60 minutes mentioned earlier).

Another important TechnicalProfile is in the RelyingParty element that defines the OutputClaims for the id-hint token.

In the last policy PasswordReset that is invoked when the user clicks on the magic link, the first OrchestrationStep in the UserJourney calls the TechnicalProfile to validate the token and extract the email.

In a following OrchestrationStep the build-in TechnicalProfile AAD-UserReadUsingEmailAddress Retrieves the account details to populate the ObjectId claim that is needed in the last step to write the new password.

Logic App Workflow

Azure Logic Apps is a cloud platform where you can create and run workflows with little to no code. The Logic App workflow comprises a sequence of actions:
- Immediate response: the first action immediately sends back a 200 OK response, ensuring that B2C doesn’t wait
- Initialization of three variables: baseUrl, id_token and the full magic link
- A GET request is sent to the GeneratePasswordToken policy endpoint
- This returns a 302 redirect with the token in Location header
- The following action is a condition that checks the status code and runs after a failure of the previous step
- The token is extracted from the location header
- The baseUrl is combined with the id_token to form the complete magic link
- You can use any email provider, like SendGrid, to send a custom branded email template with this link to the user
Now let’s dive into some meaningful details of this flow.
The BaseUrl of the magic link looks like the following. You need to replace the tenant name, the reset password policy name and client-id from the app registration for the Logic App:
1 |
"https://<YourTenantName>.b2clogin.com/<YourTenantName>.onmicrosoft.com/B2C_1A_<YourResetPasswordPolicyName>/oauth2/v2.0/authorize?client_id=<ClientIdOfLogicAppRegistration>&nonce=123&redirect_uri=https://jwt.ms&scope=openid&response_type=id_token&disable_cache=true&id_token_hint=" |
The GET request to GeneratePasswordToken with parameters.

The response of those requests with headers looks like this:

Then follows a Condition to check StatusCode == 302 with Run After configuration that continues on Has failed (all status codes other than 2xx are seen as failure).

To extract the token from response Location header we take the substring after “#id_token” fragment with the following expression:
1 |
substring(outputs('GenerateUserIdToken')['headers']?['Location'],add(indexOf(outputs('GenerateUserIdToken')['headers' ['Location'],'#id_token='),10)) |
Next steps and what to keep in mind
- Confidentiality matters: ensure that B2C doesn’t reveal account existence. There’s no validation in the policy as it always shows a generic message being “if an account exists, an email was sent”.
- Account verification layer: the Logic App should verify the account’s existence to avoid sending out emails to unknown recipients.
- Security in consumption: if you’re using a consumption plan, you should also prevent potential misuse with protections against brute force invocations, avoiding unexpected cost escalations.
- Confirmation for assurance: post a successful password reset. It’s a good practice to confirm the reset to the user. This could be another LogicApp workflow triggered by the PasswordReset policy.
Azure B2C now offers a preview feature integrating custom email verification with SendGrid. Whilst this means our manual method might become unnecessary, it does provide valuable insights into the working of custom email flows.
So overall, let’s end this guide with wishing you the best of luck and happy customizing! Users will appreciate the touch of personalization and added security. If you want more information about this topic, don’t hesitate to reach out to us.