Używanie przekierowań w usłudze MSAL Angular

Podczas korzystania z przekierowań w bibliotece MSAL konieczne jest obsługiwanie przekierowań za pomocą MsalRedirectComponent lub handleRedirectObservable.

Należy pamiętać, że poniżej dodano szczegółowe wskazówki dotyczące korzystania z biblioteki MSAL Angular z komponentami autonomicznymi w Angularze.

  1. MsalRedirectComponent
  2. Subskrybowanie ręcznie handleRedirectObservable
  3. Przekierowania ze składnikami autonomicznymi

1. MsalRedirectComponent: Dedykowany handleRedirectObservable składnik

Note

Takie podejście nie jest zgodne ze składnikami autonomicznymi platformy Angular. Aby uzyskać więcej wskazówek, zobacz sekcję dotyczącą przekierowań z autonomicznymi składnikami poniżej .

Jest to nasze zalecane podejście do obsługi przekierowań:

  • @azure/msal-angular Udostępnia dedykowany składnik przekierowania, który można zaimportować do aplikacji. Zalecamy zaimportowanie MsalRedirectComponent i zainicjowanie go wraz z AppComponent w aplikacji na app.module.ts, ponieważ pozwoli to obsłużyć wszystkie przekierowania bez konieczności ręcznego subskrybowania handleRedirectObservable() przez komponenty.
  • Strony, które chcą wykonywać działania po przekierowaniu (np. funkcje konta użytkownika, zmiany interfejsu użytkownika itp.), powinny zasubskrybować obiekt obserwowalny inProgress$, filtrując pod kątem InteractionStatus.None. Zapewni to, że podczas wykonywania funkcji nie ma żadnych interakcji. Należy pamiętać, że ostatnia / najnowsza wartość InteractionStatus będzie również dostępna podczas subskrybowania obserwowalnego obiektu inProgress$. Aby uzyskać więcej informacji na temat sprawdzania interakcji, zobacz naszą dokumentację dotyczącą zdarzeń .
  • Jeśli nie chcesz używać MsalRedirectComponent, musisz samodzielnie obsłużyć przekierowania za pomocą handleRedirectObservable(), zgodnie z opisanym poniżej podejściem.
  • Zobacz przykładowe moduły Angular , aby zapoznać się z przykładem tego podejścia.

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. Ręczne subskrybowanie handleRedirectObservable

To nie jest nasze zalecane podejście, ale jeśli nie możesz uruchomić programu MsalRedirectComponent, musisz obsługiwać przekierowania przy użyciu następującego handleRedirectObservable polecenia:

  • handleRedirectObservable() należy zasubskrybować na każdej stronie, na którą może nastąpić przekierowanie. Strony chronione przez funkcję MSAL Guard nie muszą subskrybować handleRedirectObservable()usługi , ponieważ przekierowania są przetwarzane w funkcji Guard.
  • Nie należy uzyskiwać dostępu do kont użytkowników ani wykonywać żadnych działań z nimi związanych do czasu ukończenia handleRedirectObservable(), ponieważ do tego czasu mogą nie być jeszcze w pełni uzupełnione. Ponadto jeśli interakcyjne interfejsy API są wywoływane, gdy trwa handleRedirectObservables(), spowoduje to wystąpienie błędu interaction_in_progress. Zobacz nasz dokument dotyczący zdarzeń , aby uzyskać więcej informacji na temat sprawdzania interakcji oraz naszego dokumentu dotyczącego błędów , aby uzyskać szczegółowe informacje na temat błędu interaction_in_progress .
  • Zobacz przykładowe moduły MSAL Angular , aby zapoznać się z przykładami tego podejścia.

Przykład pliku 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 Opcje

handleRedirectObservable akceptuje opcjonalny HandleRedirectPromiseOptions obiekt o następujących właściwościach:

Property Typ Description
hash string Opcjonalny skrót do przetworzenia zamiast bieżącego skrótu adresu URL.
navigateToLoginRequestUrl boolean Czy przejść do oryginalnego adresu URL żądania po przetworzeniu przekierowania. Wartość domyślna to true. Ustaw wartość na false , jeśli chcesz obsługiwać nawigację samodzielnie.

Przykład użycia:

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

Bezpośrednie przekazywanie ciągu haszującego do handleRedirectObservable(hash) jest przestarzałe. Zamiast tego użyj obiektu options: handleRedirectObservable({ hash: "#..." }).

3. Przekierowania z samodzielnymi komponentami

Ponieważ wiele aplikacji Angular korzystających z samodzielnych komponentów nie może zainicjować elementu MsalRedirectComponent, element handleRedirectObservable należy subskrybować bezpośrednio. Zalecamy zasubskrybować to w pliku app.component.ts.

  • W zależności od architektury aplikacji może być również konieczne zasubskrybowanie handleRedirectObservable() w innych obszarach.
  • Sprawdzanie trwających interakcji nadal ma zastosowanie. Więcej informacji na temat sprawdzania interakcji znajdziesz w naszym dokumencie dotyczącym zdarzeń.
  • Zobacz nasz przykład autonomiczny platformy Angular, aby zapoznać się z przykładami tego podejścia.

app.component.ts Przykład pliku:

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