Events in MSAL Angular

June 16, 2026 ยท View on GitHub

Before you start here, make sure you understand how to initialize the application object.

@azure/msal-angular uses the event system exposed by @azure/msal-browser, which emits events related to auth and MSAL, and can be used for updating UI, showing error messages, and so on.

:warning: Do not use events for telemetry. Events are intended for reacting to auth state changes in your application (e.g. updating UI or showing error messages). They are not a telemetry mechanism: the set of events, their timing, and their payloads are not guaranteed to be stable across versions, and relying on them to collect metrics or measure performance is not supported. For telemetry and performance monitoring, see MSAL Browser's Performance and telemetry configuration options.

Consuming events in your app

Events in @azure/msal-angular are managed by the MsalBroadcastService, and are available by subscribing to the msalSubject$ observable on the MsalBroadcastService.

Here is an example of how you can consume the emitted events in your application:

import { MsalBroadcastService } from '@azure/msal-angular';
import { EventMessage, EventType } from '@azure/msal-browser';

export class AppComponent implements OnInit, OnDestroy {
  private readonly _destroying$ = new Subject<void>();

  constructor(
    //...
    private msalBroadcastService: MsalBroadcastService
  ) {}

  ngOnInit(): void {
    this.msalBroadcastService.msalSubject$
      .pipe(
        // Optional filtering of events.
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS), 
        takeUntil(this._destroying$)
      )
      .subscribe((result: EventMessage) => {
        // Do something with the result
      });
  }

  ngOnDestroy(): void {
    this._destroying$.next(null);
    this._destroying$.complete();
  }
}

Note that you may need to cast the result.payload as a specific type to prevent compilation errors. The payload type will depend on the event, and can be found in our documentation here.

ngOnInit(): void {
  this.msalBroadcastService.msalSubject$
    .pipe(
      filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
    )
    .subscribe((result: EventMessage) => {
      // Casting payload as AuthenticationResult to access account
      const payload = result.payload as AuthenticationResult;
      this.authService.instance.setActiveAccount(payload.account);
    });
}

For the full example of using events, please see our sample here.

Table of events

For more information about the EventMessage object, including the full table of events currently emitted by @azure/msal-browser (including descriptions and related payloads), please see the documentation here.

Handling errors with events

As the EventError in EventMessage is defined as AuthError | Error | null, an error should be validated as the correct type before accessing specific properties on it.

See the example below of how an error can be cast to AuthError to avoid TypeScript errors:

import { MsalBroadcastService } from '@azure/msal-angular';
import { EventMessage, EventType } from '@azure/msal-browser';

export class AppComponent implements OnInit, OnDestroy {
  private readonly _destroying$ = new Subject<void>();

  constructor(
    //...
    private msalBroadcastService: MsalBroadcastService
  ) {}

  ngOnInit(): void {
    this.msalBroadcastService.msalSubject$
      .pipe(
        // Optional filtering of events
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_FAILURE), 
        takeUntil(this._destroying$)
      )
      .subscribe((result: EventMessage) => {
        if (result.error instanceof AuthError) {
          // Do something with the error
        }
      });
  }

  ngOnDestroy(): void {
    this._destroying$.next(null);
    this._destroying$.complete();
  }
}

An example of error handling can also be found on our MSAL Angular B2C Sample.

Syncing logged in state across tabs and windows

If you would like to update your UI when a user logs in or out of your app or changes the active account in a different tab or window you can subscribe to the LOGIN_SUCCESS, LOGOUT_SUCCESS, and ACTIVE_ACCOUNT_CHANGED events.

Note: Cross-tab/window event syncing in @azure/msal-browser is only enabled when cache.cacheLocation is set to localStorage. If you are using the default sessionStorage cache location, these events will not be received from other tabs or windows.

Note: For logoutRedirect(), LOGOUT_SUCCESS is only emitted/broadcast when the logout request includes an account. If your app relies on LOGOUT_SUCCESS to sync logout state across tabs or windows, pass the account you are logging out, for example logoutRedirect({ account }).

  • LOGIN_SUCCESS payloads contain the AccountInfo object for the account that signed in.
  • LOGOUT_SUCCESS payloads contain the logout request (EndSessionRequest or EndSessionPopupRequest).
  • ACTIVE_ACCOUNT_CHANGED does not include a payload (null).
import { MsalService, MsalBroadcastService } from '@azure/msal-angular';
import { EventMessage, EventType } from '@azure/msal-browser';

export class AppComponent implements OnInit, OnDestroy {
  private readonly _destroying$ = new Subject<void>();

  constructor(
    //...
    private authService: MsalService,
    private msalBroadcastService: MsalBroadcastService
  ) {}

  ngOnInit(): void {
    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS || msg.eventType === EventType.LOGOUT_SUCCESS || msg.eventType === EventType.ACTIVE_ACCOUNT_CHANGED), 
        takeUntil(this._destroying$)
      )
      .subscribe((result: EventMessage) => {
        if (this.authService.instance.getAllAccounts().length === 0) {
          // Account logged out in a different tab, redirect to homepage
          window.location.pathname = "/";
        } else {
          // Update UI to show user is signed in
        }
      });
  }

  ngOnDestroy(): void {
    this._destroying$.next(null);
    this._destroying$.complete();
  }
}

A full example can also be found in our samples.

The inProgress$ Observable

The inProgress$ observable is also handled by the MsalBroadcastService, and should be subscribed to when application needs to know the status of interactions, particularly to check that interactions are completed. We recommend checking that the status of interactions is InteractionStatus.None before functions involving user accounts.

Note that the last / most recent InteractionStatus will also be available when subscribing to the inProgress$ observable.

See the example below for its use. A full example can also be found in our samples. A full list of interaction statuses can be found here.

import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { MsalBroadcastService} from '@azure/msal-angular';
import { InteractionStatus } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
  private readonly _destroying$ = new Subject<void>();

  constructor(
    private msalBroadcastService: MsalBroadcastService
  ) {}

  ngOnInit(): void {
    this.msalBroadcastService.inProgress$
      .pipe(
        // Filtering for all interactions to be completed
        filter((status: InteractionStatus) => status === InteractionStatus.None),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        // Do something related to user accounts or UI here
      })
  }

  ngOnDestroy(): void {
    this._destroying$.next(null);
    this._destroying$.complete();
  }
}

Optional MsalBroadcastService Configurations

The MsalBroadcastService can be optionally configured to replay past events when subscribed to. By default, events that are emitted after the MsalBroadcastService is subscribed to are available. There may be instances where events prior to subscription are needed. By providing a configuration for the MsalBroadcastService and setting the eventsToReplay parameter to a number, that number of past events will be available upon subscription.

For more information about replaying events, see the RxJS docs on ReplaySubjects here.

The MsalBroadcastService can be configured in the app.module.ts file as follows:

// app.module.ts
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { MsalModule, MsalService, MsalGuard, MsalInterceptor, MsalBroadcastService, MsalRedirectComponent, MSAL_BROADCAST_CONFIG } from "@azure/msal-angular"; // Import MsalBroadcastService and MSAL_BROADCAST_CONFIG here
import { PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";

@NgModule({
    imports: [
        MsalModule.forRoot( new PublicClientApplication({ // MSAL Configuration
            auth: {
                clientId: "clientid",
                authority: "https://login.microsoftonline.com/common/",
                redirectUri: "http://localhost:4200/",
                postLogoutRedirectUri: "http://localhost:4200/",
                navigateToLoginRequestUrl: true
            },
            cache: {
                cacheLocation : BrowserCacheLocation.LocalStorage,
            },
            system: {
                loggerOptions: {
                    loggerCallback: () => {},
                    piiLoggingEnabled: false
                }
            }
        }), {
            interactionType: InteractionType.Popup, // MSAL Guard Configuration
            authRequest: {
              scopes: ['user.read']
            },
            loginFailedRoute: "/login-failed" 
        }, {
            interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
            protectedResourceMap
        })
    ],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: MsalInterceptor,
            multi: true
        },
        {
          provide: MSAL_BROADCAST_CONFIG, // Add configuration to providers here
          useValue: {
            eventsToReplay: 2 // Set how many events you want to replay when subscribing
          }
        },
        MsalGuard,
        MsalBroadcastService // Ensure the MsalBroadcastService is provided
    ],
    bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule {}