Peristiwa di MSAL Angular

Sebelum memulai di sini, pastikan Anda memahami cara menginisialisasi objek aplikasi.

@azure/msal-angular menggunakan sistem peristiwa yang diekspos oleh @azure/msal-browser, yang memancarkan peristiwa yang terkait dengan auth dan MSAL, dan dapat digunakan untuk memperbarui UI, menampilkan pesan kesalahan, dan sebagainya.

Mengkonsumsi peristiwa di aplikasi Anda

Event di @azure/msal-angular dikelola oleh MsalBroadcastService, dan tersedia dengan berlangganan ke observable msalSubject$ pada MsalBroadcastService.

Berikut adalah contoh bagaimana Anda dapat menggunakan peristiwa yang dipancarkan di aplikasi Anda:

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

Perhatikan bahwa Anda mungkin perlu mengonversi result.payload ke tipe tertentu untuk mencegah kesalahan kompilasi. Jenis payload akan tergantung pada peristiwa, dan dapat ditemukan dalam dokumentasi kami di sini.

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

Untuk contoh lengkap penggunaan peristiwa, silakan lihat sampel kami di sini.

Tabel peristiwa

Untuk informasi selengkapnya tentang EventMessage objek, termasuk tabel lengkap peristiwa yang saat ini dipancarkan oleh @azure/msal-browser (termasuk deskripsi dan payload terkait), silakan lihat dokumentasi di sini.

Menangani kesalahan dengan event

Karena EventError dalam EventMessage didefinisikan sebagai AuthError | Error | null, error harus divalidasi sebagai tipe yang benar sebelum mengakses properti tertentu padanya.

Lihat contoh di bawah ini tentang bagaimana sebuah error dapat di-cast ke AuthError untuk menghindari error 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();
  }
}

Contoh penanganan kesalahan juga dapat ditemukan pada Sampel MSAL Angular B2C kami.

Menyinkronkan log masuk status di seluruh tab dan jendela

Jika Anda ingin memperbarui UI saat pengguna masuk atau keluar dari aplikasi Anda di tab atau jendela lain, Anda dapat berlangganan ke event ACCOUNT_ADDED dan ACCOUNT_REMOVED. Payload akan berisi objek AccountInfo yang ditambahkan atau dihapus.

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

Contoh lengkap juga dapat ditemukan dalam sampel kami.

InProgress$ Dapat Diamati

Observable inProgress$ juga ditangani oleh MsalBroadcastService, dan perlu di-subscribe saat aplikasi perlu mengetahui status interaksi, terutama untuk memeriksa apakah interaksi telah selesai. Sebaiknya periksa bahwa status interaksi adalah InteractionStatus.None sebelum menjalankan fungsi yang melibatkan akun pengguna.

Perhatikan bahwa item terakhir / terbaru InteractionStatus juga akan tersedia saat berlangganan pada inProgress$ observable.

Lihat contoh di bawah ini untuk penggunaannya. Contoh lengkap juga dapat ditemukan dalam sampel kami. Daftar lengkap status interaksi dapat ditemukan di sini.

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

Konfigurasi Opsional MsalBroadcastService

MsalBroadcastService dapat dikonfigurasi secara opsional untuk memutar ulang peristiwa sebelumnya saat berlangganan. Secara bawaan, event yang dikirim setelah berlangganan ke MsalBroadcastService akan tersedia. Mungkin ada kasus ketika kejadian sebelum berlangganan diperlukan. Dengan menyediakan konfigurasi untuk MsalBroadcastService dan mengatur eventsToReplay parameter ke angka, jumlah peristiwa sebelumnya tersebut akan tersedia setelah langganan.

Untuk informasi selengkapnya tentang memutar ulang peristiwa, lihat dokumen RxJS di ReplaySubjects di sini.

MsalBroadcastService dapat dikonfigurasi dalam file app.module.ts sebagai berikut:

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