Événements dans MSAL Angular

Avant de commencer ici, assurez-vous de comprendre comment initialiser l’objet d’application.

@azure/msal-angular utilise le système d’événements exposé par @azure/msal-browser, qui émet des événements liés à l’authentification et MSAL, et peut être utilisé pour mettre à jour l’interface utilisateur, afficher les messages d’erreur, et ainsi de suite.

Consommation d’événements dans votre application

Les événements dans @azure/msal-angular sont gérés par le MsalBroadcastService, et sont disponibles en s’abonnant à l’observable msalSubject$ sur le MsalBroadcastService.

Voici un exemple de la façon dont vous pouvez consommer les événements émis dans votre 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();
  }
}

Notez qu’il se peut que vous deviez convertir le result.payload dans un type spécifique afin d’éviter les erreurs de compilation. Le type de charge utile dépend de l’événement et se trouve dans notre documentation ici.

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

Pour obtenir l’exemple complet d’utilisation d’événements, consultez notre exemple ici.

Tableau des événements

Pour plus d’informations sur l’objet EventMessage , y compris la table complète des événements actuellement émis @azure/msal-browser par (y compris les descriptions et les charges utiles associées), consultez la documentation ici.

Traitement des erreurs à l’aide d’événements

Comme le EventError dans EventMessage est défini comme étant AuthError | Error | null, il convient de vérifier qu’une erreur est du bon type avant d’accéder à ses propriétés spécifiques.

Consultez l’exemple ci-dessous montrant comment une erreur peut être convertie en AuthError afin d’éviter les erreurs TypeScript :

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

Vous trouverez également un exemple de gestion des erreurs sur notre exemple MSAL Angular B2C.

Synchronisation de l’état connecté entre les onglets et les fenêtres

Si vous souhaitez mettre à jour votre interface utilisateur lorsqu’un utilisateur se connecte ou se déconnecte de votre application dans un autre onglet ou une autre fenêtre, vous pouvez vous abonner aux événements ACCOUNT_ADDED et ACCOUNT_REMOVED. La charge utile est l’objet AccountInfo qui a été ajouté ou supprimé.

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

Vous trouverez également un exemple complet dans nos exemples.

L'Observable inProgress$

L’observable inProgress$ est également géré par le MsalBroadcastService, et doit être abonné lorsque l’application doit connaître l’état des interactions, en particulier pour vérifier que les interactions sont terminées. Nous vous recommandons de vérifier que l’état des interactions est InteractionStatus.None avant les fonctions impliquant des comptes d’utilisateur.

Notez que le dernier /le plus récent InteractionStatus sera également disponible lors de l’abonnement à l’observable inProgress$ .

Consultez l’exemple ci-dessous pour son utilisation. Vous trouverez également un exemple complet dans nos exemples. Vous trouverez ici une liste complète des états d’interaction.

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

Configurations facultatives MsalBroadcastService

Le MsalBroadcastService peut être configuré de manière optionnelle pour rejouer les événements passés lors de l’abonnement. Par défaut, les événements émis après s’être abonné à MsalBroadcastService sont disponibles. Il peut y avoir des instances où des événements avant l’abonnement sont nécessaires. En fournissant une configuration pour le MsalBroadcastService paramètre et en définissant le eventsToReplay paramètre sur un nombre, ce nombre d’événements passés sera disponible lors de l’abonnement.

Pour plus d’informations sur la façon de rejouer des événements, consultez la documentation de RxJS concernant les ReplaySubjects ici.

Le MsalBroadcastService peut être configuré dans le fichier app.module.ts comme suit :

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