Uso dei reindirizzamenti in MSAL Angular

Quando si usano i reindirizzamenti con MSAL, è obbligatorio gestire i reindirizzamenti con MsalRedirectComponent o handleRedirectObservable.

Si noti che qui sotto sono state aggiunte indicazioni specifiche per utilizzare MSAL Angular con i componenti standalone di Angular.

  1. MsalRedirectComponent
  2. Abbonarsi a handleRedirectObservable manualmente
  3. Reindirizzamenti con componenti autonomi

1. MsalRedirectComponent: Un componente dedicato handleRedirectObservable

Note

Questo approccio non è compatibile con i componenti autonomi angular. Per altre indicazioni, vedere la sezione sui reindirizzamenti con componenti autonomi di seguito .

Questo è l'approccio consigliato per la gestione dei reindirizzamenti:

  • @azure/msal-angular fornisce un componente di reindirizzamento dedicato che può essere importato nell'applicazione. Consigliamo di importare MsalRedirectComponent e inizializzarlo insieme a AppComponent nella tua applicazione sul app.module.ts, poiché questo gestirà tutti i reindirizzamenti senza che i tuoi componenti debbano iscriversi manualmente a handleRedirectObservable().
  • Le pagine che devono eseguire funzioni dopo i reindirizzamenti (ad esempio, funzioni dell'account utente, modifiche dell'interfaccia utente, ecc.) devono sottoscriversi all'observable inProgress$, filtrando per InteractionStatus.None. In questo modo si garantisce che non siano in corso interazioni durante l'esecuzione delle funzioni. Si noti che l'ultimo/più recente InteractionStatus sarà disponibile anche quando si sottoscrive l'osservabile inProgress$ . Per altre informazioni sul controllo delle interazioni, vedere la documentazione sugli eventi .
  • Se non si desidera usare MsalRedirectComponent, è necessario gestire autonomamente i reindirizzamenti con handleRedirectObservable(), come illustrato nell'approccio seguente.
  • Per un esempio di questo approccio, vedere l'esempio di moduli Angular .

msal.redirect.component.ts

// This component is part of @azure/msal-angular and can be imported and bootstrapped
import { Component, OnInit } from "@angular/core";
import { MsalService } from "./msal.service.ts";

@Component({
  selector: 'app-redirect', // Selector to be added to index.html
  template: ''
})
export class MsalRedirectComponent implements OnInit {
  
  constructor(private authService: MsalService) { }
  
  ngOnInit(): void {    
      this.authService.handleRedirectObservable().subscribe();
  }
  
}

index.html

<body>
  <app-root></app-root>
  <app-redirect></app-redirect> <!-- Selector for additional bootstrapped component -->
</body>

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';

import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';

import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation, LogLevel } from '@azure/msal-browser';
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent } from '@azure/msal-angular'; // Redirect component imported from msal-angular

export function loggerCallback(logLevel: LogLevel, message: string) {
  console.log(message);
}

export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId: '00001111-aaaa-2222-bbbb-3333cccc4444',
      redirectUri: 'http://localhost:4200',
      postLogoutRedirectUri: 'http://localhost:4200'
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
    },
    system: {
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Info,
        piiLoggingEnabled: false
      }
    }
  });
}

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read']);

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return { interactionType: InteractionType.Redirect };
}

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    ProfileComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    MatButtonModule,
    MatToolbarModule,
    MatListModule,
    HttpClientModule,
    MsalModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfigFactory
    },
    MsalService,
    MsalGuard,
    MsalBroadcastService
  ],
  bootstrap: [AppComponent, MsalRedirectComponent] // Redirect component bootstrapped here
})
export class AppModule { }

app.component.ts

import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { MsalBroadcastService, InteractionStatus } from '@azure/msal-angular';
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(
        filter((status: InteractionStatus) => status === InteractionStatus.None),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        // Do user account/UI functions here
      })
  }

2. Sottoscrivere manualmente handleRedirectObservable

Questo non è l'approccio consigliato, ma se non è possibile eseguire il bootstrap di MsalRedirectComponent, è necessario gestire i reindirizzamenti usando il handleRedirectObservable codice seguente:

  • handleRedirectObservable() deve essere registrato su ogni pagina verso cui può avvenire un reindirizzamento. Le pagine protette da MSAL Guard non devono sottoscriversi a handleRedirectObservable(), perché i reindirizzamenti sono gestiti dal Guard.
  • Non si deve accedere né eseguire alcuna azione relativa agli account utente finché handleRedirectObservable() non è completato, poiché prima di allora potrebbero non essere ancora completamente disponibili. Inoltre, se le API interattive vengono chiamate mentre handleRedirectObservables() è in corso, verrà generato un interaction_in_progress errore. Per altre informazioni sul controllo delle interazioni, vedere il documento sugli eventi e il documento sugli errori per informazioni dettagliate sull'errore interaction_in_progress .
  • Per esempi di questo approccio, vedere l'esempio di moduli Angular MSAL .

Esempio di file home.component.ts:

import { Component, OnInit } from '@angular/core';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { AuthenticationResult } from '@azure/msal-browser';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  constructor(private authService: MsalService) { }

  ngOnInit(): void {
    this.authService.handleRedirectObservable().subscribe({
      next: (result: AuthenticationResult) => {
        // Perform actions related to user accounts here
      },
      error: (error) => console.log(error)
    });
  }

}

Opzioni handleRedirectObservable

handleRedirectObservable accetta un oggetto facoltativo HandleRedirectPromiseOptions con le proprietà seguenti:

Proprietà Tipo Description
hash string Hash facoltativo da elaborare anziché l'hash dell'URL corrente.
navigateToLoginRequestUrl boolean Indica se passare all'URL della richiesta originale dopo l'elaborazione del reindirizzamento. Di default è true. Impostare su false se si desidera gestire manualmente la navigazione.

Esempio di utilizzo:

// Basic usage - processes redirect and navigates to original URL
this.authService.handleRedirectObservable().subscribe();

// Disable automatic navigation after redirect
this.authService.handleRedirectObservable({ navigateToLoginRequestUrl: false }).subscribe({
  next: (result: AuthenticationResult) => {
    if (result) {
      // Handle navigation yourself
      this.router.navigate(['/home']);
    }
  }
});

// Process a specific hash
this.authService.handleRedirectObservable({ hash: '#code=...' }).subscribe();

Note

Il passaggio di una stringa hash direttamente a handleRedirectObservable(hash) è deprecato. Usare invece l'oggetto options: handleRedirectObservable({ hash: "#..." }).

3. Reindirizzamenti con componenti autonomi

Poiché molte applicazioni Angular che usano componenti autonomi non sono in grado di eseguire il bootstrap di MsalRedirectComponent, handleRedirectObservable devono essere sottoscritte direttamente. Si consiglia di effettuare la sottoscrizione nel file app.component.ts.

  • A seconda dell'architettura dell'applicazione, potrebbe essere necessario sottoscrivere handleRedirectObservable() anche in altre aree.
  • La verifica delle interazioni in corso continua ad applicarsi; per ulteriori informazioni sulla verifica delle interazioni, consultare il nostro documento sugli eventi.
  • Per esempi di questo approccio, vedere l'esempio autonomo di Angular .

Esempio di app.component.ts file:

import { Component, OnInit, Inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    standalone: true,
    imports: [CommonModule, RouterModule]
})
export class AppComponent implements OnInit {

  constructor(
    @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
    private authService: MsalService,
    private msalBroadcastService: MsalBroadcastService
  ) {}

  ngOnInit(): void {
    this.authService.handleRedirectObservable().subscribe();
  }
}