microsoft-authentication-library-for-js
microsoft-authentication-library-for-js copied to clipboard
Can't find how to handle AADB2C90091 error code when redirected to a MSALGuard route
Core Library
MSAL.js v2 (@azure/msal-browser)
Core Library Version
2.31.0
Wrapper Library
MSAL Angular (@azure/msal-angular)
Wrapper Library Version
None
Public or Confidential Client?
Public
Description
When cancelling a workflow, like EditProfile, the redirect (back) to the app blows up when the route is MSAL guarded. No content renders as a result.
Error Message
main.js:213873
ERROR ServerError: access_denied: AADB2C90091: The user has cancelled entering self-asserted information.
Correlation ID: 8cfece7a-4f8f-4532-91ea-93d51ab85d8b Timestamp: 2023-03-16 20:28:45Z
at ServerError.AuthError [as constructor] (main.js:20566:20)
at new ServerError (main.js:21755:24)
at ResponseHandler.validateServerAuthorizationCodeResponse (main.js:23285:13)
at AuthorizationCodeClient.handleFragmentResponse (main.js:18854:21)
at RedirectHandler.<anonymous> (main.js:12341:50)
at step (main.js:1551:17)
at Object.next (main.js:1482:14)
at main.js:1454:67
at new ZoneAwarePromise (polyfills.js:9455:21)
at __awaiter (main.js:1433:10)
Msal Logs
No response
MSAL Configuration
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: environment.msalConfig.auth.clientId,
authority: environment.b2cPolicies.authorities.signIn.authority,
redirectUri: window.location.origin,
postLogoutRedirectUri: environment.msalConfig.auth.logoutRedirectUri,
knownAuthorities: [environment.b2cPolicies.authorityDomain]
},
cache: {
cacheLocation: BrowserCacheLocation.SessionStorage,
storeAuthStateInCookie: isIE, // set to true for IE 11.
},
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Verbose,
piiLoggingEnabled: false
},
},
});
}
Relevant Code Snippets
Call a policy from a guarded route
Reproduction Steps
Sign into the app. From MSAL-guarded route, trigger ViewProfile policy. On ViewProfile policy, click Exit. Returns to app, and guarded route, but nothing renders.
Expected Behavior
After cancelling a workflow, I should be able to go back to my application is if nothing happened.
Identity Provider
Azure AD / MSA
Browsers Affected (Select all that apply)
Chrome, Edge
Regression
No response
Source
External (Customer)
@doug-williamson Can you share your sample code for us to rule out any usage issues here?
@derisen any idea why this might be happening?
@doug-williamson @sameerag let me try reproducing this and get back
It's the standard Angular14B2C sample application, with HomeComponent, LoginFailedComponent and ProfileComponent, but with '' (HomeComponent) route guarded as well as ProfileComponent:
const routes: Routes = [ { path: 'profile', component: ProfileComponent, canActivate: [MsalGuard] }, { path: '', component: HomeComponent, canActivate: [MsalGuard] }, { path: 'login-failed', component: FailedComponent } ];
On Fri, Mar 17, 2023 at 4:04 PM Doğan Erişen @.***> wrote:
@doug-williamson https://github.com/doug-williamson @sameerag https://github.com/sameerag let me try reproducing this and get back
— Reply to this email directly, view it on GitHub https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5800#issuecomment-1474352320, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC7XYYPCB4FH4GY6UUVPW2TW4S7WFANCNFSM6AAAAAAV5WTFC4 . You are receiving this because you were mentioned.Message ID: <AzureAD/microsoft-authentication-library-for-js/issues/5800/1474352320@ github.com>
Thanks @derisen. If it takes too long, please let me know.
Attached is the sample application. The only thing you'll need to do is add the MsalGuard to the Home component (app-routing.module.ts) to replicate my issue. Angular14B2CApp_03172023.zip
@doug-williamson sorry for the delayed response. I can reproduce this. Looks like the Guard correctly loads the failed-component when this error occurs, but keeps navigating to the failed-component afterwards due to the cached handleRedirectPromise result from the previous request.
[Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - Guard - canActivate [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - MSAL Guard activated [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - handleRedirectPromise called [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - getAllAccounts called [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - handleRedirectPromise has been called previously, returning the result from the first call [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Error - Guard - error while logging in, unable to activate main.js:390 [Tue, 21 Mar 2023 01:28:41 GMT] : @azure/[email protected] : Verbose - Guard - loginFailedRoute set, redirecting
I need to debug further to identify the root cause and mitigate. For the moment you can workaround this by reloading the page or doing a URL navigation instead of with router, e.g:
// in app.component.ts
this.msalBroadcastService.msalSubject$
.pipe(
filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_FAILURE || msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE),
takeUntil(this._destroying$)
)
.subscribe(
(result: EventMessage) => {
if (result.error && result.error.message.indexOf('AADB2C90091') > -1) {
window.location.reload(urlToNavigate)
};
},
);
cc @jo-arroyo do you have any insights here?
@derisen Thanks for the help. @konstantin-msft Can you please check this?
Thank you @derisen. I'm a little unclear on the use of [urlToNavigate]. If I remove it and run window.location.reload(), I get the same outcome as before.
@doug-williamson Ah, my bad -it was supposed to be window.location.replace(urlToNavigate). This should refresh the page to clear the memory that's causing the issue with handleRedirectPromise. Can you confirm? I still haven't had the chance to debug further, apologies.
Hi @derisen , I'm still not able to avoid the original outcome. I've even tried window.location.replace(window.location.protocol + '//' + window.location.host);
@doug-williamson Hmm, I'm following the exact configuration you have in the sample you shared. Can we try again in this manner?
- Grab the angular-b2c-sample
- Configure
app-routing.module.tsas:
const routes: Routes = [
{
path: 'profile',
component: ProfileComponent,
canActivate: [MsalGuard]
},
{
path: '',
component: HomeComponent,
canActivate: [MsalGuard]
},
{
path: 'login-failed',
component: FailedComponent
}
];
- In
app.component.ts, update as (after line 121):
this.msalBroadcastService.msalSubject$
.pipe(
filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_FAILURE || msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE),
takeUntil(this._destroying$)
)
.subscribe((result: EventMessage) => {
if (result.error && result.error.message.indexOf('AADB2C90091') > -1) {
window.location.replace("http://localhost:4200/");
};
// Check for forgot password error
// Learn more about AAD error codes at https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes
if (result.error && result.error.message.indexOf('AADB2C90118') > -1) {
let resetPasswordFlowRequest: RedirectRequest | PopupRequest = {
authority: environment.b2cPolicies.authorities.resetPassword.authority,
scopes: [],
};
this.login(resetPasswordFlowRequest);
};
});
- Sign-in to the app.
- Select Hello API button on the navbar. App navigates to "/profile" route.
- Select Edit Profile button on the navbar. Browser loads edit profile page.
- Select cancel. App now tries to navigate to "/login-failed" route
- Immediately after login-failed browser navigates back to "/"
- You can now navigate to "/profile" route again using the angular router (i.e. the guard doesn't block you)
To be clear this is just a workaround. The issue you ran into is indeed a bug in the SDK. The internal redirectResponse map still holds the original error in memory after the Guard navigates the app to the "/login-failed" route, and when you try to navigate to another page via angular router, handleRedirectPromise keeps returning the same error, which causes the Guard to throw.
Thank you @derisen. I'm out of the office this week, on vacation. I wanted to let you know in the event that there's inactivity on this work item.
@derisen I was able to confirm that your mitigation works w/ the sample app.
Should we track the original bug, cc @jo-arroyo?
HI @derisen. I'm still running into difficulties with implementing this workaround w/ a more complex setup.
In this case, the HomeComponent is a parent component for a handful of others. It's route guarded, and every time I'm navigated back to the app (from MSAL) it starts diving into the sub routes of HomeComponent, and then it triggers the navigation to login-failed. The MsalBroadcastService isn't picking up the LOGIN_FAILURE to trigger the window.location.replace.
My configuration in AppModule appears to be the same as the sample app. Any ideas or suggestions?
@doug-williamson I see that you've set the MsalGuard on the home route. Just curious if you've seen or tried our solution for handling guarding the home route?
HI @jo-arroyo. Are you referring to the PathLocationStrategy or calling handleRedirectObservable()?
@doug-williamson I am referring to these instructions:
For those using the PathLocationStrategy, we recommend:
- Setting the MsalGuard on your initial page
- Set your redirectUri to 'http://localhost:4200/auth'
- Adding an 'auth' path to your routes, setting the MsalRedirectComponent as the component (this route should not be protected by the MsalGuard)
- Making sure the MsalRedirectComponent is bootstrapped
- Optionally: adding MsalGuard to all your routes if you want all your routes protected
Have you tried this? Does adding the auth path change the behavior for you?
Hi @jo-arroyo. No, that didn't seem to help.
It might be worth noting that the versions I'm using for a larger project are:
"@azure/msal-angular": "^2.4.6",
"@azure/msal-browser": "^2.31.0",
Do we know if this has been implemented in either V2 or V3 yet?
@doug-williamson Apologies, this fix has not yet been prioritized. We will update this thread when we have a PR.
@jo-arroyo Can we expect any progress on the case? This problem still occurs in v3. And this workaround (https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/4378#issuecomment-1163969575) is no longer working (from @azure/msal-angular v2.5.7), since there is no property called redirectResponse in the PublicClientApplication class.
The codes from ClientApplication class have been moved to StandardController class, but you can't override handleRedirectPromise function of the controller, since the StandardController class is not exported from @azure/msal-browser module.
@jo-arroyo Any updates on this?
@galfimihaly Mind sharing how you have handled it in the meantime? Same happened with msal-react upgrade to v2.x. I've simply called instance.handleRedirectPromise().catch(() => { do whatever} ) so far directly after initializing the msal instance, which has worked, but I doubt it's the correct way to actually prevent this.
@clbrec I have been looking for a solution to this problem for a long time. According to the official recommendation: To handle this error, fetch the error description for the user and respond back with a new authentication request using the same user flow.
But I didn't want to re-authenticate a user who was already logged in, so I stuck with this solution:
this.msalBroadcastService.msalSubject$
.pipe(
filter(
(msg: EventMessage) =>
msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE,
),
)
.subscribe((msg: EventMessage) => {
const errorMessage = (msg.error as AuthError).errorMessage;
if (errorMessage) {
// Handle ERROR ServerError: access_denied: AADB2C90091: The user has cancelled entering self-asserted information
if (errorMessage.indexOf('AADB2C90091') > -1) {
this.authService.instance.loginRedirect();
}
}
});
The loginRedirect() method detects that the user has a valid access token in local storage, so they stay logged in, and redirects to the url specified in the redirectUri config parameter.
Azure B2C How to handle when a user clicks cancel Microsoft Learn - User cancelled the operation Microsoft Q&A - Cancel button Azure B2C
I have a other way to fix it temporary
- use Custom page in User flows
- hide the cancel link
#cancel { display: none; } - add a new link to your home page
<a alt="backlink" id="backA" href="http://xxxxxx.com" style="display:block;width:100%;text-align:left;margin-bottom:20px">Cancel</a>
Do we know if this is either addressed in V3 or going to be addressed anytime soon? Also, when is V3 going to be upgraded to ng17?