AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.69k stars 2.65k forks source link

(Cancel button) Error - Guard - error while logging in, unable to activate #4378

Open Jacooscript opened 2 years ago

Jacooscript commented 2 years ago

Core Library

MSAL.js v2 (@azure/msal-browser)

Core Library Version

2.21.0

Wrapper Library

MSAL Angular (@azure/msal-angular)

Wrapper Library Version

2.1.0

Description

I am integrating B2C the same way as it's described in https://github.com/Azure-Samples/ms-identity-javascript-angular-tutorial/tree/main/3-Authorization-II/2-call-api-b2c in my project

My app has "/panel/settings" Component, which is protected by MsalGuard:

const panelRoutes = [
  { path: '', component: PanelHomeComponent },
  { path: 'settings', component: PanelSettingsComponent },
];

const routes: Routes = [
  { path: 'panel', component: PanelComponent, canActivate: [MsalGuard], children: panelRoutes },
];

I implemented EditProfile button (redirect) there. It works ok when I update the profile (it redirects me to "/panel/settings" without any issues). But there is a problem when I click a Cancel button (default EditProfile user flow). Logs: image

Steps and redirects:

  1. I click the Cancel button on the default EditProfile B2C page
  2. Auto-redirect to https://localhost:4200/
  3. Auto-redirect to https://localhost:4200/panel/settings
  4. Auto-redirect to https://localhost:4200/ (wrong)

When I try to click Login button again on the landing page after all auto-redirects: image

Angular: 13.0.2 .NET Core: 6

MSAL Configuration is the same as in https://github.com/Azure-Samples/ms-identity-javascript-angular-tutorial/tree/main/3-Authorization-II/2-call-api-b2c (different clientId, etc params ofc)

MSAL Configuration

No response

Relevant Code Snippets

No response

Identity Provider

Azure B2C Basic Policy

Source

Internal (Microsoft)

tnorling commented 2 years ago

@EvgenStudent Looks like you've configured a loginFailedRoute on your MsalGuard which is redirecting you back to the homepage when you've clicked the cancel button. What is the behavior you expect to see?

Jacooscript commented 2 years ago

@tnorling Even if I remove loginFailedRoute in my MsalGuard, everything works the same, but now my landing page is blank after all redirects. I think, since EditProfile is canceled (== auth is failed), it can not activate the guard. But I already have an ActiveAccount in MsalService created by SignUpSignIn policy.

Behavior what I expect is redirect to the initial "/panel/settings" (where EditProfile button is) after EditProfile cancelation.

tnorling commented 2 years ago

@EvgenStudent Thanks for clarifying. I was able to reproduce this behavior and agree this is something we can and should improve. I've marked this as a bug and added an item in our backlog to investigate the best way to solve this as this would affect any scenario where at least one user is signed-in and an interactive request fails. In the meantime I would suggest copying the Guard and modifying the behavior here to catch this error and let the guard activate in this scenario. We'll update this issue when we have an official solution

rebendajirijr commented 2 years ago

We are also facing this issue. Modified version of the original guard seems as possible workaround till the issue is resolved here.

sunilshrestha commented 2 years ago

Same issue with password reset. Because of the way the redirect response handled in this scenario the handleRedirectPromise always returns the existing response which is AuthError received from the redirection. There might be a proper way to handle but for quick fix this custom implementation might help.

Override the handleRedirectPromise method and check the error code and delete the response cache if the error exists in existing redirect response.

export class CustomPublicClientApplication extends PublicClientApplication implements IPublicClientApplication { override async handleRedirectPromise(hash?: string): Promise<AuthenticationResult | null> { let response = this.redirectResponse.get(hash || Constants.EMPTY_STRING); if (typeof response !== "undefined") { response.then(x => { }).catch(error => { if (error instanceof AuthError) { if (error.errorMessage.indexOf("AADB2C90091") > -1) { this.redirectResponse.delete(hash); } } }); } return super.handleRedirectPromise(hash) } }

Use this CustomPublicClientApplication class as your MSAL instance provider factory.

justin-rhoades-stg commented 2 years ago

@tnorling Just for clarification on your suggestion. You are suggesting we rebuild the MSAL Guard and add the catchError on the handleRedirectObservable? Also what page should get the handle handleRedirectObservable? The page that initiated the password reset or the page that we are redirecting to.

Thank you for your time!

tnorling commented 2 years ago

Just for clarification on your suggestion. You are suggesting we rebuild the MSAL Guard and add the catchError on the handleRedirectObservable?

Correct. The current behavior of the MSAL Guard naiivly assumes that when a redirect returns an error it means that the user should not have access to the guarded route. This is not an accurate assumption when trying to sign a 2nd user in or when working with B2C where you might be invoking interaction to go to the profile edit or password reset policy rather than to sign a user in. You should modify the existing MSAL Guard catchError handler to check whether or not a user was already signed in when this error was thrown. If there is a user signed in you can activate the Guard - if not you can use the existing behavior to render the login failed route.

Also what page should get the handle handleRedirectObservable? The page that initiated the password reset or the page that we are redirecting to.

Short answer: both. Long answer: handleRedirectObservable should be run on all pages that will be hit after the redirect to the IDP. By default handleRedirectObservable called on the redirectUri page will redirect you to the calling page and then handleRedirectObservable called on the calling page will handle the response.

justin-rhoades-stg commented 2 years ago

@tnorling Thanks for answering that question thoroughly and quickly. Would this error error while logging in, unable to activate mean something happened up stream from the guard which is logging this line only when it catches the error?

tnorling commented 2 years ago

It indicates that loginPopup or loginRedirect/handleRedirectPromise threw an error which resulted in the user not being logged in

AntonEvseev commented 1 year ago

Any update on the issue? In this sample the same bug. image

doug-williamson commented 1 year ago

Is this bug still outstanding?

anfasci commented 1 year ago

Any solution for [Error - Guard - error while logging in, unable to activate ???

galfimihaly commented 1 year ago

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.

lamelyan commented 1 year ago

The "unable to active" is not the actual error. If you want to see the actual error, enable PII logging.

When you create the PublicClientApplication there should be a setting for loggerOptions > piiLoggingEnabled. Enable it.

Once PII logging is on, the guard will log the actual error.

Note: PII stands for personally identifiable information and should be turned off for production.

manuel103 commented 9 months ago

After enabling piiLoggingEnabled as suggested by @lamelyan , the actual error was:

Error - Guard - error: pkce_not_created: The PKCE code challenge and verifier could not be generated.

The solution was to enable SSL on the server and making sure the site is served over HTTPS.