Ereignisse in MSAL Angular

Bevor Sie hier beginnen, stellen Sie sicher, dass Sie wissen, wie Sie das Anwendungsobjekt initialisieren.

@azure/msal-angular verwendet das von @azure/msal-browser bereitgestellte Ereignissystem, das Ereignisse im Zusammenhang mit Authentifizierung und MSAL auslöst und zum Aktualisieren der Benutzeroberfläche, zum Anzeigen von Fehlermeldungen usw. verwendet werden kann.

Verarbeiten von Ereignissen in Ihrer App

Ereignisse in @azure/msal-angular werden von MsalBroadcastService verwaltet und sind durch das Abonnieren des Observables msalSubject$ auf MsalBroadcastService verfügbar.

Hier ist ein Beispiel dafür, wie Sie die ausgegebenen Ereignisse in Ihrer Anwendung nutzen können:

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();
  }
}

Beachten Sie, dass Sie den result.payload Typ möglicherweise als bestimmten Typ umwandeln müssen, um Kompilierungsfehler zu vermeiden. Der Nutzlasttyp hängt vom Ereignis ab und finden Sie in unserer Dokumentation hier.

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);
    });
}

Das vollständige Beispiel für die Verwendung von Ereignissen finden Sie in unserem Beispiel hier.

Ereignisverzeichnis

Weitere Informationen zum EventMessage Objekt, einschließlich der vollständigen Tabelle der derzeit ausgegebenen @azure/msal-browser Ereignisse (einschließlich Beschreibungen und zugehöriger Nutzlasten), finden Sie in der Dokumentation hier.

Behandeln von Fehlern mit Ereignissen

Da das EventError in EventMessage als AuthError | Error | null definiert ist, sollte ein Fehler auf den korrekten Typ geprüft werden, bevor auf bestimmte Eigenschaften zugegriffen wird.

Im folgenden Beispiel sehen Sie, wie ein Fehler zu AuthError gecastet werden kann, um TypeScript-Fehler zu vermeiden:

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();
  }
}

Ein Beispiel für die Fehlerbehandlung finden Sie auch in unserem MSAL Angular B2C-Beispiel.

Synchronisierung des Anmeldestatus über Registerkarten und Fenster hinweg

Wenn Sie Ihre Benutzeroberfläche aktualisieren möchten, wenn sich ein Benutzer in einer anderen Registerkarte oder einem anderen Fenster bei Ihrer App an- oder abmeldet, können Sie die Ereignisse ACCOUNT_ADDED und ACCOUNT_REMOVED abonnieren. Die Nutzlast ist das AccountInfo Objekt, das hinzugefügt oder entfernt wurde.

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.authService.instance.enableAccountStorageEvents(); // Register the storage listener that will be emitting the events
    this.msalBroadcastService.msalSubject$
      .pipe(
        // Optional filtering of events
        filter((msg: EventMessage) => msg.eventType === EventType.ACCOUNT_ADDED || msg.eventType === EventType.ACCOUNT_REMOVED), 
        takeUntil(this._destroying$)
      )
      .subscribe((result: EventMessage) => {
        if (this.authService.msalInstance.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. result.payload contains the account that was logged in
        }
      });
  }

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

Ein vollständiges Beispiel finden Sie auch in unseren Beispielen.

Der inProgress$ Observable

Das inProgress$ wird auch von MsalBroadcastService verarbeitet und sollte abonniert werden, wenn die Anwendung den Status von Interaktionen kennen muss, insbesondere um zu prüfen, ob Interaktionen abgeschlossen sind. Wir empfehlen, zu prüfen, dass der Status der Interaktionen InteractionStatus.None ist, bevor Sie Funktionen verwenden, die Benutzerkonten betreffen.

Beachten Sie, dass die letzte / neueste InteractionStatus auch verfügbar ist, wenn Sie das inProgress$-Observable abonnieren.

Sehen Sie sich das folgende Beispiel für die Verwendung an. Ein vollständiges Beispiel finden Sie auch in unseren Beispielen. Eine vollständige Liste der Interaktionsstatus finden Sie hier.

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();
  }
}

Optionale MsalBroadcastService Konfigurationen

Das MsalBroadcastService kann optional so konfiguriert werden, dass beim Abonnieren frühere Ereignisse erneut wiedergegeben werden. Standardmäßig sind Ereignisse verfügbar, die ausgelöst werden, nachdem MsalBroadcastService abonniert wurde. Es kann Vorkommen geben, in denen Ereignisse vor dem Abonnement benötigt werden. Durch Die Bereitstellung einer Konfiguration für den MsalBroadcastService Parameter und das Festlegen des eventsToReplay Parameters auf eine Zahl ist diese Anzahl vergangener Ereignisse im Abonnement verfügbar.

Weitere Informationen zum Wiedergeben von Ereignissen finden Sie in den RxJS-Dokumenten zu ReplaySubjects hier.

Dies MsalBroadcastService kann in der datei app.module.ts wie folgt konfiguriert werden:

// 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 {}