Gebeurtenissen in MSAL Angular

Voordat u hier begint, moet u weten hoe u het toepassingsobject initialiseert.

@azure/msal-angular maakt gebruik van het gebeurtenissysteem dat wordt weergegeven door @azure/msal-browser, waarmee gebeurtenissen worden verzonden die betrekking hebben op verificatie en MSAL, en kan worden gebruikt voor het bijwerken van de gebruikersinterface, het weergeven van foutberichten, enzovoort.

Events in uw app verwerken

Gebeurtenissen in @azure/msal-angular worden beheerd door de MsalBroadcastService, en zijn beschikbaar door zich te abonneren op de msalSubject$ waarneembare op de MsalBroadcastService.

Hier volgt een voorbeeld van hoe u de verzonden gebeurtenissen in uw toepassing kunt gebruiken:

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

Mogelijk moet u het result.payload als een specifiek type casten om compilatiefouten te voorkomen. Het payloadtype hangt af van de gebeurtenis en is te vinden in onze documentatie 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);
    });
}

Zie ons voorbeeld hier voor het volledige voorbeeld van het gebruik van gebeurtenissen.

Tabel met gebeurtenissen

Raadpleeg voor meer informatie over het EventMessage-object, inclusief de volledige tabel met gebeurtenissen die momenteel door @azure/msal-browser worden uitgezonden (inclusief beschrijvingen en gerelateerde payloads), de documentatie hier.

Fouten afhandelen met gebeurtenissen

Aangezien de EventError in EventMessage is gedefinieerd als AuthError | Error | null, moet een foutobject eerst als het juiste type worden gevalideerd voordat u toegang krijgt tot de specifieke eigenschappen ervan.

Zie hieronder een voorbeeld van hoe een fout naar AuthError kan worden gecast om TypeScript-fouten te voorkomen:

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

Een voorbeeld van foutafhandeling vindt u ook in ons MSAL Angular B2C-voorbeeld.

De inlogstatus synchroniseren tussen tabbladen en vensters

Als u uw gebruikersinterface wilt bijwerken wanneer een gebruiker zich in een ander tabblad of venster bij uw app aan- of afmeldt, kunt u luisteren naar de gebeurtenissen ACCOUNT_ADDED en ACCOUNT_REMOVED. De payload is het object AccountInfo dat werd toegevoegd of verwijderd.

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

Een volledig voorbeeld vindt u ook in onze voorbeelden.

De inProgress$ Observable

De inProgress$ observable wordt ook afgehandeld door de MsalBroadcastService, en erop moet worden geabonneerd wanneer de toepassing de status van interacties moet kennen, met name om te controleren of de interacties zijn voltooid. We raden aan te controleren of de status van interacties InteractionStatus.None is voordat functies met gebruikersaccounts worden uitgevoerd.

Houd er rekening mee dat de laatste/meest recente InteractionStatus ook beschikbaar is wanneer u zich abonneert op het inProgress$ waarneembare.

Zie het onderstaande voorbeeld voor het gebruik ervan. Een volledig voorbeeld vindt u ook in onze voorbeelden. Hier vindt u een volledige lijst met interactiestatussen.

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

Optionele MsalBroadcastService configuraties

De MsalBroadcastService optie kan eventueel worden geconfigureerd om eerdere gebeurtenissen opnieuw af te spelen wanneer u zich abonneert op. Standaard zijn gebeurtenissen die worden uitgezonden nadat er op MsalBroadcastService is geabonneerd, beschikbaar. Er zijn mogelijk gevallen waarin gebeurtenissen voorafgaand aan het abonnement nodig zijn. Door een configuratie op te geven voor de MsalBroadcastService parameter en de eventsToReplay parameter in te stellen op een getal, is dat aantal eerdere gebeurtenissen beschikbaar voor het abonnement.

Zie de RxJS-documenten over ReplaySubjects hier voor meer informatie over het opnieuw afspelen van gebeurtenissen.

De MsalBroadcastService kan als volgt worden geconfigureerd in het app.module.ts-bestand:

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