1

I'm building a angular 16 spa. When I try to add Msal 3.0 (beta) it throws a BrowserAuthError: native_broker_called_before_initialize Error. I tried to "migrate" the Msal Tutorial from Microsoft to Standalone Components. Has someone an complete example using MSAL with Angular Standalone Components and Pop-up login?

My important files:

main.ts

bootstrapApplication(AppComponent, appConfig)
  .then()
  .catch((err) => console.error(err));

app.config.ts

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import {
  provideRouter,
  withComponentInputBinding,
  withDisabledInitialNavigation,
  withRouterConfig,
} from '@angular/router';

import { routes } from './app.routes';
import { provideAnimations } from '@angular/platform-browser/animations';
import {
  HTTP_INTERCEPTORS,
  HttpClientModule,
  provideHttpClient,
  withInterceptors,
} from '@angular/common/http';
import { MatSnackBar } from '@angular/material/snack-bar';
import {
  MAT_DIALOG_DATA,
  MatDialog,
  MatDialogRef,
} from '@angular/material/dialog';
import { MatNativeDateModule } from '@angular/material/core';

import {
  MsalModule,
  MsalRedirectComponent,
  MsalGuard,
  MsalInterceptor,
  MSAL_GUARD_CONFIG,
  MSAL_INTERCEPTOR_CONFIG,
  MsalService,
  MsalBroadcastService,
  MSAL_INSTANCE,
} from '@azure/msal-angular';
import { InteractionType, PublicClientApplication } from '@azure/msal-browser';
import { AdminGuard, AdminGuardConfig } from './AdminGuard';

const isIE =
  window.navigator.userAgent.indexOf('MSIE ') > -1 ||
  window.navigator.userAgent.indexOf('Trident/') > -1;
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(
      routes,
      withComponentInputBinding(),
      withDisabledInitialNavigation()
    ),
    provideAnimations(),
    provideHttpClient(withInterceptors([])),
    importProvidersFrom(MatNativeDateModule, MsalGuard),

    {
      provide: MSAL_GUARD_CONFIG,
      useValue: AdminGuardConfig,
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useValue: {
        interactionType: InteractionType.Redirect,
        protectedResourceMap: new Map([
          ['https://graph.microsoft.com/v1.0/me', ['user.read']],
          ['http://localhost:4200/events/create', ['admin']],
        ]),
      },
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    {
      provide: MSAL_INSTANCE,
      useValue: new PublicClientApplication({
        auth: {
          clientId: 'clientId', // Application (client) ID from the app registration
          authority:
            'https://login.microsoftonline.com/tenantId', // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
          redirectUri: 'http://localhost:4200/events/check/overview', // This is your redirect URI
        },
        cache: {
          cacheLocation: 'localStorage',
          storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
        },
      }),
    },
    {
      provide: PublicClientApplication,
      useValue: new PublicClientApplication({
        auth: {
          clientId: 'clientId', // Application (client) ID from the app registration
          authority:
            'https://login.microsoftonline.com/tenantId', // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
          redirectUri: 'http://localhost:4200/events/check/overview', // This is your redirect URI
        },
        cache: {
          cacheLocation: 'localStorage',
          storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
        },
      }),
    },
    MsalService,
    MsalGuard,
    MsalBroadcastService

 ],
};

app.component.ts

import { Component, Inject } from '@angular/core';
import { CommonModule, NgIf } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { NavigationComponent } from './common/navigation/navigation.component';
import { MatCardModule } from '@angular/material/card';
import { provideHttpClient, withFetch } from '@angular/common/http';
import {
  MSAL_GUARD_CONFIG,
  MsalBroadcastService,
  MsalGuardConfiguration,
  MsalModule,
  MsalRedirectComponent,
  MsalService,
} from '@azure/msal-angular';
import {
  InteractionStatus,
  PublicClientApplication,
  RedirectRequest,
} from '@azure/msal-browser';
import { Subject, filter, takeUntil } from 'rxjs';

const isIE =
  window.navigator.userAgent.indexOf('MSIE ') > -1 ||
  window.navigator.userAgent.indexOf('Trident/') > -1;
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    CommonModule,
    RouterOutlet,
    NavigationComponent,
    MatCardModule,
    NgIf,
  ],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  title = 'MsalTest';
  isIframe = false;
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();
  constructor(
    @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
    private broadcastService: MsalBroadcastService,
    private authService: MsalService,
    private instance: PublicClientApplication
  ) {}

  ngOnInit() {
    this.isIframe = window !== window.parent && !window.opener;

    this.broadcastService.inProgress$
      .pipe(
        filter(
          (status: InteractionStatus) => status === InteractionStatus.None
        ),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        this.setLoginDisplay();
      });
  }

  login() {
    // await this.instance.initialize();
    this.authService.loginPopup().subscribe({
      next: (result) => {
        console.log(result);
        this.setLoginDisplay();
      },
      error: (error) => console.error(error),
    });
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
}
TheLegend27
  • 73
  • 1
  • 10

2 Answers2

1

I just followed this example and everything is now working: https://github.com/mdddev/msal-b2c/blob/main/angular15-msal3-standalone

TheLegend27
  • 73
  • 1
  • 10
0

The native broker needs to be disabled in your MSAL config when creating the MSAL instance:

 provide: MSAL_INSTANCE,
  useValue: new PublicClientApplication({
    auth: {
      clientId: 'clientId', // Application (client) ID from the app registration
      authority:
        'https://login.microsoftonline.com/tenantId', // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
      redirectUri: 'http://localhost:4200/events/check/overview', // This is your redirect URI
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
    },
    system: {
      allowNativeBroker: false, // Disables WAM Broker
    }
  })
  • Found this solution by looking in the Azure AD Sign-in logs, it showed "Failures" for certain logins with the message: "This web native bridge interrupt will be shown to the userduring login when the application is requesting login through the native broker and needs eSTS to ensure the broker is properly configured" – Wesley Van den Eede Jul 20 '23 at 12:32