Integrate Azure AD B2C reset password user flow in angular using oidc-client-js.

Image for post
Image for post
Photo by Wiredsmart from Pexels

This post continues from previous posts which I go over using oidc-client-js to interact with azure adb2c:

In this post, I’m going to share how to handle resetting password.

You can find the sample project here.

Create the reset password user flow

At the time of writing, Azure ADB2C has a predefined, standard version of the reset password flow. However, Microsoft recommends using a new version of the reset password flow, as Microsoft no longer updates and maintains the standard versions. For more info, checkout the document.

If you forgot how to create a new user flow, checkout the document, or look at my previous post. Once you have created the flow, make note of the URL as that is where you will send the user to reset the password.

Reference the reset password user flow

In the sample app, the codes handle building the full URL based on the known pattern, as the below snippets show.

get resetPasswordRoute() {
return this.buildRoute({ policyName: this.resetPasswordPolicy });
}
private buildRoute(options: RouteBuilderOptions): string {
const url = new URL(
`https://${this.tenantName}.b2clogin.com/${this.tenantName}.onmicrosoft.com/oauth2/v2.0/authorize`
);
url.searchParams.append('p', options.policyName);
url.searchParams.append('client_id', this.client_id);
url.searchParams.append('nonce', 'defaultNonce');
url.searchParams.append(
'redirect_uri',
options.redirectUri ? options.redirectUri : this.redirect_uri
);
url.searchParams.append('scope', options.scope ? options.scope : 'openid');
url.searchParams.append(
'response_type',
options.responseType ? options.responseType : 'id_token'
);
if (options.promptLogin) {
url.searchParams.append('prompt', 'login');
}
return url.href;
}

In the above snippets, value of resetPasswordPolicy comes from the oidc-settings.json file under the assets folder, as shown below.

{
"client_id": "47ea6724-b21f-46de-9d17-7425920f77e4",
"signupSigninPolicy": "b2c_1_signupandsignin",
"tenantName": "taithienbo",
"response_type": "id_token token",
"loadUserInfo": false,
"response_mode": "fragment",
"scope": "https://taithienbo.onmicrosoft.com/mywebapi/user_impersonation openid profile",
"editProfilePolicy": "B2C_1_a_profile_edit",
"resetPasswordPolicy": "B2C_1_Password_Reset_2"
}

Handle Forgot Password

The sign up and sign in user flow has the Forgot Password link which the user can click on to reset the password. Clicking on the link brings the user back to the app via the redirect url which you can specify as part of the settings you pass into the constructor of the UserManager object. For more information, checkout my previous post, or oidc-client-js document.

Image for post
Image for post

When redirecting the user back to the app, azure includes the hint that the user has forgotten the password in the url fragment, as in the following example url:

http://localhost:4200/#error=access_denied&error_description=AADB2C90118:+The+user+has+forgotten+their+password.%0D%0ACorrelation+ID:+be33615b-3c47-46aa-add6-6cb2e158bace%0D%0ATimestamp:+2020-11-28+18:56:49Z%0D%0A&state=cf99e9fb510740278ef8636618bc830e

Based on the error code and message in the fragment, we can detect and react to the forgot password scenario, as the below snippets demonstrate.

ngOnInit() {
....
const idTokenKeyWord = 'id_token';
const errorDescriptionKeyWord = 'error_description';
const resetPasswordCode = 'AADB2C90118';
const idToken = params.get(idTokenKeyWord);
this.route.fragment.subscribe((fragment) => {
const params = new URLSearchParams(fragment);
const errorDescription = params.get(errorDescriptionKeyWord);
if (idToken) {
this.handleIdToken();
}
else if (
errorDescription &&
errorDescription.includes(resetPasswordCode)
) {
this.handlePasswordReset();
}
});
}
});
}

private handlePasswordReset() {
// we simply redirect the user to the reset password page.
window.location.href = this.authService.settings.resetPasswordRoute;
}

On password reset success

Once the user has successfully reset the password and come back to the app, azure adb2c redirects the user back to the app. In the browser url, the id token is present. However, from my experience, we cannot do a silent login although we have the id token. Silent login works for when the user has finished editing the profile. But for resetting the password, the user needs to login again.

private handleIdToken() {
// If the user has come back from the edit profile page, the
// user object is still present in the storage, and we can do a
// silent login to pick up any changes to the profile if desired. However,
// if the user has reset the password, the user object is no longer
// available, and the user needs to login again.
this.authService.loadUser().then((user) => {
if (user) {
// user has come back after edit profile.
this.authService.loginSilent().then((u) => {
this.user = u;
});
} else {
// user has come back after reset password and need to login again.
this.authService.loginRedirect();
}
});
}

That’s pretty much it. Feel free to reach out if you have any questions.

Happy Coding.

References

oidc-client-js github wiki page
Tutorial: Create user flows in Azure Active Directory B2C
User flow versions in Azure Active Directory B2C

Originally published at https://www.taithienbo.com on November 29, 2020.

Written by

Backend developer in .NET core. I enjoy the outdoor, hanging out with good friends, reading and personal development.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store