Uso de redirecciones en MSAL Angular

Al usar redireccionamientos con MSAL, es obligatorio gestionar los redireccionamientos con MsalRedirectComponent o con handleRedirectObservable.

Tenga en cuenta que se han agregado instrucciones específicas para usar MSAL Angular con componentes independientes de Angular a continuación.

  1. MsalRedirectComponent
  2. Suscribirse a handleRedirectObservable manualmente
  3. Redirecciones con componentes independientes

1. MsalRedirectComponent: Un componente dedicado handleRedirectObservable

Note

Este enfoque no es compatible con los componentes independientes de Angular. Consulte la sección sobre redirecciones con componentes independientes a continuación para obtener más instrucciones.

Este es nuestro enfoque recomendado para controlar los redireccionamientos:

  • @azure/msal-angular proporciona un componente de redirección dedicado que se puede importar en la aplicación. Recomendamos importar MsalRedirectComponent e inicializarlo junto con AppComponent en el app.module.ts de la aplicación, ya que esto gestionará todas las redirecciones sin necesidad de que los componentes se suscriban manualmente a handleRedirectObservable().
  • Las páginas que deseen ejecutar acciones después de redirecciones (p. ej., funciones de la cuenta de usuario, cambios en la interfaz de usuario, etc.) deben suscribirse al observable inProgress$, filtrando por InteractionStatus.None. Esto garantizará que no haya interacciones en curso al realizar las funciones. Tenga en cuenta que el último o más reciente InteractionStatus también estará disponible al suscribirse al observable inProgress$. Consulte nuestra documentación sobre eventos para obtener más información sobre cómo comprobar las interacciones.
  • Si no desea usar MsalRedirectComponent, debe controlar las redirecciones con handleRedirectObservable() usted mismo, tal como se indica en el enfoque siguiente.
  • Consulte nuestro ejemplo de módulos de Angular para obtener un ejemplo de este enfoque.

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. Suscribirse a handleRedirectObservable manualmente

Este no es nuestro enfoque recomendado, pero si no puede arrancar el MsalRedirectComponent, debe gestionar las redirecciones usando el handleRedirectObservable de la siguiente manera:

  • handleRedirectObservable() debe estar suscrito en cada página a la que se pueda redirigir. Las páginas protegidas por MSAL Guard no necesitan suscribirse a handleRedirectObservable(), ya que las redirecciones se procesan en Guard.
  • No se debe acceder ni realizar ninguna acción relacionada con las cuentas de usuario hasta que handleRedirectObservable() se complete, ya que es posible que no esté totalmente completado hasta entonces. Además, si se llama a las API interactivas mientras handleRedirectObservables() está en curso, se producirá un interaction_in_progress error. Consulte nuestro documento sobre eventos para obtener más información sobre cómo comprobar las interacciones y nuestro documento sobre los errores para obtener más información sobre el interaction_in_progress error.
  • Consulte nuestro ejemplo de módulos de MSAL Angular para obtener ejemplos de este enfoque.

Ejemplo de archivo 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)
    });
  }

}

handleRedirectObservable Opciones

handleRedirectObservable acepta un objeto opcional HandleRedirectPromiseOptions con las siguientes propiedades:

Propiedad Tipo Description
hash string Hash opcional para procesar en lugar del hash de dirección URL actual.
navigateToLoginRequestUrl boolean Indica si se va a ir a la dirección URL de solicitud original después de procesar el redireccionamiento. Tiene como valor predeterminado true. false Establézcalo en si desea controlar la navegación usted mismo.

Ejemplo de uso:

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

Pasar una cadena hash directamente a handleRedirectObservable(hash) está en desuso. Use el objeto options en su lugar: handleRedirectObservable({ hash: "#..." }).

3. Redirecciones con componentes independientes

Dado que muchas aplicaciones Angular que usan componentes independientes no pueden inicializar el MsalRedirectComponent, hay que suscribirse directamente a handleRedirectObservable. Nuestra recomendación es suscribirse a ella en el archivo app.component.ts.

  • En función de la arquitectura de la aplicación, es posible que también tenga que suscribirse a handleRedirectObservable() en otras áreas.
  • La comprobación de interacciones en curso todavía se aplica, consulte nuestro documento sobre eventos para obtener más información sobre cómo comprobar las interacciones.
  • Consulte nuestro ejemplo independiente de Angular para obtener ejemplos de este enfoque.

Ejemplo de app.component.ts archivo:

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