Usando redirecionamentos no MSAL Angular

Ao usar redirecionamentos com MSAL, é obrigatório lidar com redirecionamentos com o MsalRedirectComponent ou handleRedirectObservable.

Note que orientações específicas foram adicionadas abaixo para usar o MSAL Angular com componentes autônomos do Angular.

  1. MsalRedirectComponent
  2. Inscrevendo-se em handleRedirectObservable manualmente
  3. Redirecionamentos com componentes autônomos

1. MsalRedirectComponent: um componente dedicado handleRedirectObservable

Note

Essa abordagem não é compatível com componentes autônomos angulares. Consulte a seção sobre redirecionamentos com componentes autônomos abaixo para obter mais diretrizes.

Essa é nossa abordagem recomendada para lidar com redirecionamentos:

  • @azure/msal-angular fornece um componente de redirecionamento dedicado que pode ser importado para seu aplicativo. Recomendamos importar o MsalRedirectComponent e inicializá-lo junto com AppComponent no seu aplicativo na app.module.ts, pois isso cuidará de todos os redirecionamentos sem que seus componentes precisem se inscrever em handleRedirectObservable() manualmente.
  • As páginas que desejam executar funções após redirecionamentos (por exemplo, funções de conta de usuário, alterações na interface do usuário etc.) devem se inscrever no observável inProgress$, filtrando por InteractionStatus.None. Isso garantirá que não haja interações em andamento ao executar as funções. Observe que o último/mais recente InteractionStatus também estará disponível ao inscrever-se no observável inProgress$. Consulte nossa documentação sobre eventos para obter mais informações sobre como verificar interações.
  • Se você não quiser usar o MsalRedirectComponent, deverá lidar com os redirecionamentos por conta própria com handleRedirectObservable(), conforme descrito na abordagem abaixo.
  • Consulte nosso exemplo de módulos Angular para obter um exemplo dessa abordagem.

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. Inscrever-se em handleRedirectObservable manualmente

Essa não é a nossa abordagem recomendada, mas, se você não conseguir inicializar o MsalRedirectComponent, deverá lidar com os redirecionamentos usando o handleRedirectObservable da seguinte forma:

  • A inscrição no handleRedirectObservable() deve ser feita em todas as páginas para as quais possa ocorrer um redirecionamento. As páginas protegidas pelo MSAL Guard não precisam se inscrever em handleRedirectObservable(), pois os redirecionamentos são processados no Guard.
  • O acesso ou a execução de qualquer ação relacionada a contas de usuário não deve ser feito até que handleRedirectObservable() seja concluído, pois ele pode não estar totalmente preenchido até lá. Além disso, se as APIs interativas forem chamadas enquanto handleRedirectObservables() estiver em andamento, isso resultará em um erro interaction_in_progress. Consulte nosso documento sobre eventos para obter mais informações sobre como verificar interações e nosso documento sobre erros para obter detalhes sobre o interaction_in_progress erro.
  • Consulte nosso exemplo de módulos do MSAL Angular para obter exemplos dessa abordagem.

Exemplo de arquivo 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 opções

handleRedirectObservable aceita um objeto opcional HandleRedirectPromiseOptions com as seguintes propriedades:

Propriedade Tipo Description
hash string Hash opcional a ser processado em vez do hash de URL atual.
navigateToLoginRequestUrl boolean Se deseja navegar até a URL de solicitação original depois de processar o redirecionamento. Usa true como padrão. Defina como false se você quiser lidar com a navegação por conta própria.

Exemplo 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

Passar uma string de hash diretamente para handleRedirectObservable(hash) foi descontinuado. Em vez disso, use o objeto options: handleRedirectObservable({ hash: "#..." }).

3. Redirecionamentos com componentes autônomos

Como muitos aplicativos do Angular que usam componentes autônomos não conseguem inicializar o MsalRedirectComponent, a inscrição no handleRedirectObservable deve ser feita diretamente. Nossa recomendação é se inscrever nela no arquivo app.component.ts.

  • Dependendo da arquitetura da aplicação, talvez você também precise se inscrever em handleRedirectObservable() em outras partes.
  • A verificação de interações em andamento ainda se aplica; consulte nosso documento sobre eventos para mais informações sobre como verificar interações.
  • Consulte nosso exemplo independente em Angular para ver exemplos dessa abordagem.

Exemplo de app.component.ts arquivo:

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