Omleidingen gebruiken in MSAL Angular

Wanneer u omleidingen met MSAL gebruikt, is het verplicht om omleidingen te verwerken met de MsalRedirectComponent of handleRedirectObservable.

Houd er rekening mee dat hieronder specifieke richtlijnen zijn toegevoegd voor het gebruik van MSAL Angular met zelfstandige Angular-onderdelen.

  1. MsalRedirectComponent
  2. Abonneren op handleRedirectObservable handmatig
  3. Omleidingen met zelfstandige onderdelen

1. MsalRedirectComponent: Een toegewezen handleRedirectObservable onderdeel

Note

Deze benadering is niet compatibel met zelfstandige Angular-onderdelen. Zie de sectie over omleidingen met zelfstandige onderdelen hieronder voor meer informatie.

Dit is onze aanbevolen aanpak voor het afhandelen van omleidingen:

  • @azure/msal-angular biedt een speciaal omleidingsonderdeel dat kan worden geïmporteerd in uw toepassing. We raden aan de MsalRedirectComponent te importeren en deze naast AppComponent te initialiseren in uw toepassing op de app.module.ts, omdat hierdoor alle omleidingen worden afgehandeld zonder dat uw componenten zich handmatig op handleRedirectObservable() hoeven te abonneren.
  • Pagina's die functies willen uitvoeren na redirects (bijvoorbeeld gebruikersaccountfuncties, UI-wijzigingen enzovoort) moeten zich abonneren op de inProgress$-observable, gefilterd op InteractionStatus.None. Dit zorgt ervoor dat er geen interacties worden uitgevoerd bij het uitvoeren van de functies. Houd er rekening mee dat de laatste/meest recente InteractionStatus ook beschikbaar is wanneer u zich abonneert op het inProgress$ waarneembare. Raadpleeg onze documentatie over gebeurtenissen voor meer informatie over het controleren op interacties.
  • Als u het MsalRedirectComponent niet wilt gebruiken, moet u omleidingen met handleRedirectObservable() zelf afhandelen, zoals uiteengezet in de onderstaande aanpak.
  • Bekijk het voorbeeld van onze Angular-modules voor een voorbeeld van deze benadering.

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. Abonneren op handleRedirectObservable handmatig

Dit is niet onze aanbevolen aanpak, maar als u de MsalRedirectComponent niet kunt initialiseren, moet u omleidingen met de handleRedirectObservable als volgt afhandelen:

  • handleRedirectObservable() moet geabonneerd zijn op elke pagina waarnaar kan worden omgeleid. Pagina’s die door de MSAL Guard worden beveiligd, hoeven zich niet te abonneren op handleRedirectObservable(), omdat redirects door de Guard worden verwerkt.
  • Toegang tot gebruikersaccounts of het uitvoeren van een actie ermee mag pas plaatsvinden nadat handleRedirectObservable() is voltooid, omdat deze vóór die tijd mogelijk nog niet volledig is ingevuld. Bovendien, als interactieve API's worden aangeroepen terwijl handleRedirectObservables() wordt uitgevoerd, leidt dit tot een interaction_in_progress-fout. Zie ons document over gebeurtenissen voor meer informatie over het controleren op interacties en ons document over fouten voor meer informatie over de interaction_in_progress fout.
  • Bekijk het voorbeeld van MSAL Angular-modules voor voorbeelden van deze benadering.

Voorbeeld van home.component.ts bestand:

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 Opties

handleRedirectObservable accepteert een optioneel HandleRedirectPromiseOptions object met de volgende eigenschappen:

Property Type Description
hash string Optionele hash die moet worden verwerkt in plaats van de huidige URL-hash.
navigateToLoginRequestUrl boolean Of u naar de oorspronkelijke aanvraag-URL wilt navigeren nadat de omleiding is verwerkt. Wordt standaard ingesteld op true. Stel deze false optie in als u zelf navigatie wilt afhandelen.

Voorbeeldgebruik:

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

Het rechtstreeks doorgeven van een hashtekenreeks aan handleRedirectObservable(hash) wordt afgeraden. Gebruik in plaats daarvan het optieobject: handleRedirectObservable({ hash: "#..." }).

3. Omleidingen met zelfstandige onderdelen

Aangezien veel Angular-toepassingen met standalone componenten de MsalRedirectComponent niet kunnen bootstrapen, moet er rechtstreeks op handleRedirectObservable worden geabonneerd. Wij raden u aan om u erop te abonneren in het app.component.ts bestand.

  • Afhankelijk van uw toepassingsarchitectuur moet u zich mogelijk ook in andere gebieden op handleRedirectObservable() abonneren.
  • Controleren op actieve interacties is nog steeds van toepassing. Raadpleeg ons document over gebeurtenissen voor meer informatie over het controleren op interacties.
  • Bekijk ons zelfstandige Angular-voorbeeld voor voorbeelden van deze benadering.

Voorbeeld van app.component.ts bestand:

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