azure-ad-msalmsal-angular

Issue logging into Azure B2C Angular application using @azure/msal-angular


I'm having an odd error logging my app into B2C using auth code flow with PKCE using the @azure/msal-angular library. I'm redirecting back to the B2C login, get redirected back to my app, my app successfully gets a token back from the token api but then a JS error is logged and prevents my app from continuing to load. Here is the console output:

[Thu, 27 Jan 2022 22:34:53 GMT] : @azure/msal-angular@2.1.0 : Info - Interceptor - 1 scopes found for endpoint
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Info - Interceptor - 1 scopes found for endpoint
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Info - Interceptor - 1 scopes found for endpoint
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Info - Interceptor - 1 scopes found for endpoint
core.js:40853 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:handleRedirectStart
core.js:40853 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Error - Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginStart
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Error - Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginStart
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Error - Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginStart
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-angular@2.1.0 : Error - Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginStart
4msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginFailure
4core.js:6241 ERROR Error: Uncaught (in promise): BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.  For more visit: aka.ms/msaljs/browser-errors.
BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.  For more visit: aka.ms/msaljs/browser-errors.
    at BrowserAuthError.AuthError [as constructor] (AuthError.js:27:1)
    at new BrowserAuthError (BrowserAuthError.js:169:1)
    at Function.push.../../node_modules/@azure/msal-browser/dist/error/BrowserAuthError.js.BrowserAuthError.createInteractionInProgressError (BrowserAuthError.js:236:1)
    at RedirectClient.<anonymous> (StandardInteractionClient.js:216:51)
    at step (_tslib.js:75:1)
    at Object.next (_tslib.js:56:45)
    at _tslib.js:49:1
    at new ZoneAwarePromise (zone-evergreen.js:960:1)
    at __awaiter (_tslib.js:45:1)
    at RedirectClient.push.../../node_modules/@azure/msal-browser/dist/interaction_client/StandardInteractionClient.js.StandardInteractionClient.preflightInteractiveRequest (StandardInteractionClient.js:207:25)
    at resolvePromise (zone-evergreen.js:798:1)
    at zone-evergreen.js:705:1
    at zone-evergreen.js:721:1
    at ZoneDelegate.invoke (zone-evergreen.js:364:1)
    at Object.onInvoke (core.js:41667:1)
    at ZoneDelegate.invoke (zone-evergreen.js:363:1)
    at Zone.run (zone-evergreen.js:123:1)
    at zone-evergreen.js:857:1
    at ZoneDelegate.invokeTask (zone-evergreen.js:399:1)
    at Object.onInvokeTask (core.js:41645:1)
defaultErrorLogger @ core.js:6241
handleError @ core.js:6294
next @ core.js:42627
schedulerFn @ core.js:37132
__tryOrUnsub @ Subscriber.js:183
next @ Subscriber.js:122
_next @ Subscriber.js:72
next @ Subscriber.js:49
next @ Subject.js:39
emit @ core.js:37092
(anonymous) @ core.js:41707
invoke @ zone-evergreen.js:364
run @ zone-evergreen.js:123
runOutsideAngular @ core.js:41501
onHandleError @ core.js:41704
handleError @ zone-evergreen.js:368
runGuarded @ zone-evergreen.js:136
api.microtaskDrainDone @ zone-evergreen.js:670
drainMicroTaskQueue @ zone-evergreen.js:576
Promise.then (async)
scheduleMicroTask @ zone-evergreen.js:552
scheduleTask @ zone-evergreen.js:388
onScheduleTask @ zone-evergreen.js:272
scheduleTask @ zone-evergreen.js:378
scheduleTask @ zone-evergreen.js:210
scheduleMicroTask @ zone-evergreen.js:230
scheduleResolveOrReject @ zone-evergreen.js:847
resolvePromise @ zone-evergreen.js:785
(anonymous) @ zone-evergreen.js:705
webpackJsonpCallback @ bootstrap:25
(anonymous) @ mcl-portal-order.js:1
capture_page_info.js:954 Gather controls: 2.934814453125 ms
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : [64b25cee-dd82-4679-8a9e-b4eb65289ff3] : @azure/msal-common@6.0.0 : Info - in acquireToken call
DevTools failed to load source map: Could not load content for chrome-extension://lkoeejijapdihgbegpljiehpnlkadljb/browser-polyfill.js.map: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME
client:52 [WDS] Live Reloading enabled.
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:loginSuccess
msal-config.module.ts:27 [Thu, 27 Jan 2022 22:34:54 GMT] : @azure/msal-browser@2.21.0 : Info - Emitting event: msal:handleRedirectEnd

If refresh my app after this happens the app loads fine and calls all my api endpoints appropriately using the token it got. So it's just the inital process of bootstraping my app, redirecting to the login, getting redirected back to my app, posting the token request then something fails inbetween the time the token is received but before the api requests are made?

Have I configured this wrong?

my MsalConfigModule that sets up the configuration of the MsalModule:

const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; // Remove this line to use Angular Universal

export function loggerCallback(logLevel: LogLevel, message: string) {
  console.log(message);
}

export function MSALInstanceFactory(config: ConfigService): IPublicClientApplication {

  const authConf = config.getSettings()['oauthConfig'][0] as any;

  const authConfig = new PublicClientApplication({
    auth: {
      clientId: authConf['client_id'],
      authority: authConf.authority,
      knownAuthorities: [authConf.authority],
      redirectUri: authConf['redirect_uri'],
      postLogoutRedirectUri: authConf['redirect_uri'],
      navigateToLoginRequestUrl: true
    },
    cache:{
      cacheLocation: BrowserCacheLocation.SessionStorage,
      storeAuthStateInCookie: isIE
    },
    system: {
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Info,
        piiLoggingEnabled: false
      }
    }
  });

  return authConfig;
}

export function MSALInterceptorConfigFactory(config: ConfigService): MsalInterceptorConfiguration {
  const resourceMap = new Map<string, Array<string>>();
  // iterate over the urlMappings config and create a protectedresourcemap
  const configUrls = config.getSettings()['urlMappings'] as Array<any>;
  const scope = config.getSettings()['oauthConfig'][0].scope as string;
  configUrls.map(urlMap => resourceMap.set(`${urlMap.url}/*`, [scope]));

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap: resourceMap
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect
  };
}

@NgModule({
  providers: [],
  imports: [MsalModule]
})
export class MsalConfigModule {

  static forRoot() {
    return {
      ngModule: MsalConfigModule,
      providers: [
        {
          provide: MSAL_INSTANCE,
          useFactory: MSALInstanceFactory,
          deps: [ConfigService]
        },
        {
          provide: MSAL_GUARD_CONFIG,
          useFactory: MSALGuardConfigFactory,
          deps: [ConfigService]
        },
        {
          provide: MSAL_INTERCEPTOR_CONFIG,
          useFactory: MSALInterceptorConfigFactory,
          deps: [ConfigService]
        },
        MsalService,
        MsalGuard,
        MsalBroadcastService,
        {
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true
        }
      ]
    };
  }
}

part of my main app module:

@NgModule({
  declarations: [AppComponent],
    imports: [
      ...
      MsalConfigModule.forRoot()
    ],
    providers: [
      DatePipe,
      SlicePipe
    ],
    bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}

Solution

  • The solution to this was for me to add the

     canActivate:[MsalGuard]
    

    guard to all my routes. I think the underlying problem was that the interceptor was looking for a token to send in the api requests before we had a token to send. Adding the guard prevented the activation of the route until we are authenticated and then once the the route was activated my api requests would fire, the interceptors would have thier token to send along and everything just worked.

    I guess the take way is that you do need to add the MsalGuard, it's not enough to provide the interceptors and configure the protected resource map.