Esercitazione: Accedere agli utenti e chiamare l'API Microsoft Graph da un'applicazione a pagina singola Angular usando il flusso del codice di autenticazione

In questa esercitazione si creerà un'applicazione a pagina singola Angular che consente agli utenti di accedere e chiamare l'API Microsoft Graph usando il flusso del codice di autorizzazione con PKCE. L'applicazione a pagina singola creata usa Microsoft Authentication Library (MSAL) per Angular v2.

Contenuto dell'esercitazione:

  • Registrare l'applicazione nell'interfaccia di amministrazione di Microsoft Entra
  • Creare un progetto Angular con npm
  • Aggiungere il codice per supportare l'accesso e la disconnessione
  • Aggiungere il codice per chiamare l'API Microsoft Graph
  • Testare l'app

MSAL Angular v2 migliora MSAL Angular v1 supportando il flusso del codice di autorizzazione nel browser invece del flusso di concessione implicita. MSAL Angular v2 non supporta il flusso implicito.

Prerequisiti

  • Node.js per l'esecuzione di un server Web locale.
  • Visual Studio Code o un altro editor per la modifica dei file di progetto.

Funzionamento dell'app di esempio

Diagram showing the authorization code flow in a single-page application

L'applicazione di esempio creata in questa esercitazione consente a un'applicazione a pagina singola Angular di eseguire query sull'API Microsoft Graph o su un'API Web che accetta token rilasciati da Microsoft Identity Platform. Usa Microsoft Authentication Library (MSAL) per Angular v2, un wrapper della libreria MSAL.js v2. MSAL Angular consente alle applicazioni Angular 9+ di autenticare gli utenti aziendali usando l'ID Microsoft Entra e anche gli utenti con account Microsoft e identità di social networking come Facebook, Google e LinkedIn. La libreria consente inoltre di ottenere l'accesso a servizi cloud Microsoft e a Microsoft Graph.

Per questo scenario, dopo l'accesso di un utente, viene richiesto un token di accesso che viene aggiunto a richieste HTTP tramite l'intestazione dell'autorizzazione. L'acquisizione e il rinnovo dei token vengono gestiti da Microsoft Authentication Library (MSAL).

Librerie

Questa esercitazione usa le librerie seguenti:

Libreria Descrizione
MSAL Angular Microsoft Authentication Library per il wrapper Angular JavaScript
MSAL Browser Pacchetto del browser Microsoft Authentication Library per JavaScript v2

È possibile trovare il codice sorgente per tutte le librerie MSAL.js nel microsoft-authentication-library-for-js repository su GitHub.

Ottenere l'esempio di codice completo

Si preferisce scaricare invece il progetto di esempio completato per questa esercitazione? Clonare ms-identity-javascript-angular-spa

git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git

Per continuare con l'esercitazione e compilare manualmente l'applicazione, passare alla sezione successiva, Registrare l'applicazione e gli identificatori di record.

Registrare l'applicazione e gli identificatori di record

Suggerimento

I passaggi descritti in questo articolo possono variare leggermente in base al portale da cui si inizia.

Per completare la registrazione, specificare un nome per l'applicazione, specificare i tipi di account supportati e aggiungere un URI di reindirizzamento. Dopo la registrazione, nel riquadro Panoramica dell'applicazione vengono visualizzati gli identificatori necessari nel codice sorgente dell'applicazione.

  1. Accedere all'interfaccia di amministrazione di Microsoft Entra come almeno uno sviluppatore di applicazioni.
  2. Se si ha accesso a più tenant, usare l'icona Impostazioni nel menu in alto per passare al tenant in cui si vuole registrare l'applicazione dal menu Directory e sottoscrizioni.
  3. Passare a Applicazioni> di identità>Registrazioni app.
  4. Seleziona Nuova registrazione.
  5. Immettere un nome per l'applicazione, ad esempio Angular-SPA-auth-code.
  6. Per Tipi di account supportati selezionare Account solo in questa directory dell'organizzazione. Per informazioni sui diversi tipi di account, selezionare l'opzione ?
  7. In URI di reindirizzamento (facoltativo) usare il menu a discesa per selezionare Applicazione a pagina singola (SPA) e immettere http://localhost:4200 nella casella di testo.
  8. Selezionare Registra.
  9. Al termine della registrazione, viene visualizzato il riquadro Panoramica dell'applicazione. Registrare l'ID directory (tenant) e l'ID applicazione (client) da usare nel codice sorgente dell'applicazione.

Creare il progetto

  1. Aprire Visual Studio Code, selezionare File>Apri cartella.... Passare a e selezionare il percorso in cui creare il progetto.

  2. Aprire un nuovo terminale selezionando Terminale>Nuovo terminale.

    1. Potrebbe essere necessario cambiare i tipi di terminale. Selezionare la freccia giù accanto all'icona + nel terminale e selezionare Prompt dei comandi.
  3. Eseguire i comandi seguenti per creare un nuovo progetto Angular con il nome msal-angular-tutorial, installare librerie dei componenti di Angular Material, MSAL Browser, MSAL Angular e generare componenti home e profilo.

    npm install -g @angular/cli
    ng new msal-angular-tutorial --routing=true --style=css --strict=false
    cd msal-angular-tutorial
    npm install @angular/material @angular/cdk
    npm install @azure/msal-browser @azure/msal-angular
    ng generate component home
    ng generate component profile
    

Configurare l'applicazione e modificare l'interfaccia utente di base

  1. Aprire src/app/app.module.ts. MsalInterceptor E MsalModule devono essere aggiunti insieme alla importsisIE costante . Si aggiungeranno anche i moduli materiali. Sostituire l'intero contenuto del file con il frammento di codice seguente:

    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 { MsalModule, MsalRedirectComponent } from "@azure/msal-angular";
    import { PublicClientApplication } from "@azure/msal-browser";
    
    const isIE =
      window.navigator.userAgent.indexOf("MSIE ") > -1 ||
      window.navigator.userAgent.indexOf("Trident/") > -1;
    
    @NgModule({
      declarations: [AppComponent, HomeComponent, ProfileComponent],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot(
          new PublicClientApplication({
            auth: {
              clientId: "Enter_the_Application_Id_here", // Application (client) ID from the app registration
              authority:
                "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here", // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
              redirectUri: "Enter_the_Redirect_Uri_Here", // This is your redirect URI
            },
            cache: {
              cacheLocation: "localStorage",
              storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
            },
          }),
          null,
          null
        ),
      ],
      providers: [],
      bootstrap: [AppComponent, MsalRedirectComponent],
    })
    export class AppModule {}
    
  2. Sostituire i valori seguenti con i valori ottenuti dall'interfaccia di amministrazione di Microsoft Entra. Per altre informazioni sulle opzioni configurabili disponibili, vedere Inizializzare le applicazioni client.

    • clientId - Identificatore dell'applicazione, detto anche client. Sostituire Enter_the_Application_Id_Here con il valore ID applicazione (client) registrato in precedenza dalla pagina di panoramica dell'applicazione registrata.
    • authority - Questo è composto da due parti:
      • L'istanza è l'endpoint del provider di servizi cloud. Per il cloud globale o principale di Azure, immettere https://login.microsoftonline.com. Verificare i diversi endpoint disponibili nei cloud nazionali.
      • L'ID tenant è l'identificatore del tenant in cui è registrata l'applicazione. Sostituire con _Enter_the_Tenant_Info_Here il valore ID directory (tenant) registrato in precedenza dalla pagina di panoramica dell'applicazione registrata.
    • redirectUri : il percorso in cui il server di autorizzazione invia l'utente dopo che l'app è stata autorizzata correttamente e ha concesso un codice di autorizzazione o un token di accesso. Sostituisci Enter_the_Redirect_Uri_Here con http://localhost:4200.
  3. Aprire src/app/app-routing.module.ts e aggiungere route ai componenti home e profilo . Sostituire l'intero contenuto del file con il frammento di codice seguente:

    import { NgModule } from "@angular/core";
    import { Routes, RouterModule } from "@angular/router";
    import { BrowserUtils } from "@azure/msal-browser";
    import { HomeComponent } from "./home/home.component";
    import { ProfileComponent } from "./profile/profile.component";
    
    const routes: Routes = [
      {
        path: "profile",
        component: ProfileComponent,
      },
      {
        path: "",
        component: HomeComponent,
      },
    ];
    
    const isIframe = window !== window.parent && !window.opener;
    
    @NgModule({
      imports: [
        RouterModule.forRoot(routes, {
          // Don't perform initial navigation in iframes or popups
          initialNavigation:
            !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
              ? "enabledNonBlocking"
              : "disabled", // Set to enabledBlocking to use Angular Universal
        }),
      ],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}
    
  4. Aprire src/app/app.component.html e sostituire il codice esistente con il codice seguente:

    <mat-toolbar color="primary">
      <a class="title" href="/">{{ title }}</a>
    
      <div class="toolbar-spacer"></div>
    
      <a mat-button [routerLink]="['profile']">Profile</a>
    
      <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
    
    </mat-toolbar>
    <div class="container">
      <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
      <router-outlet *ngIf="!isIframe"></router-outlet>
    </div>
    
  5. Aprire src/style.css per definire il codice CSS:

    @import "~@angular/material/prebuilt-themes/deeppurple-amber.css";
    
    html,
    body {
      height: 100%;
    }
    body {
      margin: 0;
      font-family: Roboto, "Helvetica Neue", sans-serif;
    }
    .container {
      margin: 1%;
    }
    
  6. Aprire src/app/app.component.css per aggiungere stili CSS all'applicazione:

    .toolbar-spacer {
      flex: 1 1 auto;
    }
    
    a.title {
      color: white;
    }
    

Accedere con popup

  1. Aprire src/app/app.component.ts e sostituire il contenuto del file con il comando seguente per accedere a un utente usando una finestra popup:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
    
      constructor(private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      }
    
      login() {
        this.authService.loginPopup()
          .subscribe({
            next: (result) => {
              console.log(result);
              this.setLoginDisplay();
            },
            error: (error) => console.log(error)
          });
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    

Accedere con i reindirizzamenti

  1. Aggiornare src/app/app.module.ts per eseguire il bootstrap di MsalRedirectComponent. Si tratta di un componente di reindirizzamento dedicato, che gestisce i reindirizzamenti. Modificare l'importazione e AppComponent il MsalModule bootstrap in modo che sia simile al seguente:

    ...
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    ...
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    ...
    
  2. Aprire src/index.html e sostituire l'intero contenuto del file con il frammento di codice seguente, che aggiunge il <app-redirect> selettore:

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>msal-angular-tutorial</title>
      <base href="/">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
    <body>
      <app-root></app-root>
      <app-redirect></app-redirect>
    </body>
    </html>
    
  3. Aprire src/app/app.component.ts e sostituire il codice con il codice seguente per accedere a un utente usando un reindirizzamento full-frame:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
    
      constructor(private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      }
    
      login() {
        this.authService.loginRedirect();
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    
  4. Passare a src/app/home/home.component.ts e sostituire l'intero contenuto del file con il frammento di codice seguente per sottoscrivere l'evento LOGIN_SUCCESS :

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
    
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          )
          .subscribe((result: EventMessage) => {
            console.log(result);
          });
      }
    }
    

Rendering condizionale

Per eseguire il rendering di determinati utenti dell'interfaccia utente solo per gli utenti autenticati, i componenti devono sottoscrivere MsalBroadcastService per verificare se gli utenti hanno eseguito l'accesso e l'interazione è stata completata.

  1. MsalBroadcastService Aggiungere a src/app/app.component.ts e sottoscrivere l'osservabile inProgress$ per verificare se l'interazione è completa e un account è connesso prima del rendering dell'interfaccia utente. Il codice ora dovrebbe essere simile al seguente:

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { MsalService, MsalBroadcastService } from '@azure/msal-angular';
    import { InteractionStatus } from '@azure/msal-browser';
    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 {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        this.authService.loginRedirect();
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    
  2. Aggiornare il codice in src/app/home/home.component.ts per verificare anche se l'interazione deve essere completata prima dell'aggiornamento dell'interfaccia utente. Il codice ora dovrebbe essere simile al seguente:

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
      loginDisplay = false;
    
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
    
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          )
          .subscribe((result: EventMessage) => {
            console.log(result);
          });
    
        this.msalBroadcastService.inProgress$
          .pipe(
            filter((status: InteractionStatus) => status === InteractionStatus.None)
          )
          .subscribe(() => {
            this.setLoginDisplay();
          })
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    
  3. Sostituire il codice in src/app/home/home.component.html con le visualizzazioni condizionali seguenti:

    <div *ngIf="!loginDisplay">
        <p>Please sign-in to see your profile information.</p>
    </div>
    
    <div *ngIf="loginDisplay">
        <p>Login successful!</p>
        <p>Request your profile information by clicking Profile above.</p>
    </div>
    

Implementare Angular Guard

La MsalGuard classe è una che è possibile usare per proteggere le route e richiedere l'autenticazione prima di accedere alla route protetta. I passaggi seguenti aggiungono alla MsalGuardProfile route. La protezione della Profile route significa che, anche se un utente non esegue l'accesso usando il Login pulsante , se tenta di accedere Profile alla route o seleziona il Profile pulsante, MsalGuard chiede all'utente di eseguire l'autenticazione tramite popup o reindirizzamento prima di visualizzare la Profile pagina.

MsalGuard è una classe di praticità che è possibile usare per migliorare l'esperienza utente, ma non deve essere basata sulla sicurezza. Gli utenti malintenzionati possono potenzialmente aggirare le guardie lato client e assicurarsi che il server non restituisca dati a cui l'utente non deve accedere.

  1. Aggiungere la MsalGuard classe come provider nell'applicazione in src/app/app.module.ts e aggiungere le configurazioni per .MsalGuard Gli ambiti necessari per acquisire i token in un secondo momento possono essere forniti in authRequeste il tipo di interazione per Guard può essere impostato su Redirect o Popup. Il codice dovrebbe essere simile al seguente:

    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 {
      MsalModule,
      MsalRedirectComponent,
      MsalGuard,
    } from "@azure/msal-angular"; // MsalGuard added to imports
    import {
      PublicClientApplication,
      InteractionType,
    } from "@azure/msal-browser"; // InteractionType added to imports
    
    const isIE =
      window.navigator.userAgent.indexOf("MSIE ") > -1 ||
      window.navigator.userAgent.indexOf("Trident/") > -1;
    
    @NgModule({
      declarations: [AppComponent, HomeComponent, ProfileComponent],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot(
          new PublicClientApplication({
            auth: {
              clientId: "Enter_the_Application_Id_here",
              authority:
                "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here",
              redirectUri: "Enter_the_Redirect_Uri_Here",
            },
            cache: {
              cacheLocation: "localStorage",
              storeAuthStateInCookie: isIE,
            },
          }),
          {
            interactionType: InteractionType.Redirect, // MSAL Guard Configuration
            authRequest: {
              scopes: ["user.read"],
            },
          },
          null
        ),
      ],
      providers: [
        MsalGuard, // MsalGuard added as provider here
      ],
      bootstrap: [AppComponent, MsalRedirectComponent],
    })
    export class AppModule {}
    
  2. Impostare nelle MsalGuard route da proteggere in src/app/app-routing.module.ts:

    import { NgModule } from "@angular/core";
    import { Routes, RouterModule } from "@angular/router";
    import { BrowserUtils } from "@azure/msal-browser";
    import { HomeComponent } from "./home/home.component";
    import { ProfileComponent } from "./profile/profile.component";
    import { MsalGuard } from "@azure/msal-angular";
    
    const routes: Routes = [
      {
        path: "profile",
        component: ProfileComponent,
        canActivate: [MsalGuard],
      },
      {
        path: "",
        component: HomeComponent,
      },
    ];
    
    const isIframe = window !== window.parent && !window.opener;
    
    @NgModule({
      imports: [
        RouterModule.forRoot(routes, {
          // Don't perform initial navigation in iframes or popups
          initialNavigation:
            !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
              ? "enabledNonBlocking"
              : "disabled", // Set to enabledBlocking to use Angular Universal
        }),
      ],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}
    
  3. Modificare le chiamate di accesso in src/app/app.component.ts in modo da tenere conto del authRequest set nelle configurazioni di protezione. Il codice dovrebbe ora essere simile al seguente:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
    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 {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
        } else {
          this.authService.loginRedirect();
        }
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    

Acquisire un token

Intercettore Angular

Microsoft Authentication Library per Angular fornisce una classe Interceptor che acquisisce automaticamente i token per le richieste in uscita che usano il client http Angular per le risorse protette note.

  1. Aggiungere la Interceptor classe come provider all'applicazione in src/app/app.module.ts, con le relative configurazioni. Il codice dovrebbe ora essere simile al seguente:

    import { BrowserModule } from "@angular/platform-browser";
    import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
    import { NgModule } from "@angular/core";
    import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; // Import
    
    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 {
      MsalModule,
      MsalRedirectComponent,
      MsalGuard,
      MsalInterceptor,
    } from "@azure/msal-angular"; // Import MsalInterceptor
    import {
      InteractionType,
      PublicClientApplication,
    } from "@azure/msal-browser";
    
    const isIE =
      window.navigator.userAgent.indexOf("MSIE ") > -1 ||
      window.navigator.userAgent.indexOf("Trident/") > -1;
    
    @NgModule({
      declarations: [AppComponent, HomeComponent, ProfileComponent],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        HttpClientModule,
        MsalModule.forRoot(
          new PublicClientApplication({
            auth: {
              clientId: "Enter_the_Application_Id_Here",
              authority:
                "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here",
              redirectUri: "Enter_the_Redirect_Uri_Here",
            },
            cache: {
              cacheLocation: "localStorage",
              storeAuthStateInCookie: isIE,
            },
          }),
          {
            interactionType: InteractionType.Redirect,
            authRequest: {
              scopes: ["user.read"],
            },
          },
          {
            interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
            protectedResourceMap: new Map([
              ["Enter_the_Graph_Endpoint_Here/v1.0/me", ["user.read"]],
            ]),
          }
        ),
      ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true,
        },
        MsalGuard,
      ],
      bootstrap: [AppComponent, MsalRedirectComponent],
    })
    export class AppModule {}
    

    Le risorse protette vengono fornite come .protectedResourceMap Gli URL specificati nella raccolta protectedResourceMap fanno distinzione tra maiuscole e minuscole. Per ogni risorsa, aggiungere gli ambiti da restituire nel token di accesso.

    Ad esempio:

    • ["user.read"] per Microsoft Graph
    • ["<Application ID URL>/scope"] per le API Web personalizzate, ovvero api://<Application ID>/access_as_user

    Modificare i valori in protectedResourceMap come descritto di seguito:

    • Enter_the_Graph_Endpoint_Here è l'istanza dell'API Microsoft Graph con cui l'applicazione dovrà comunicare. Per l'endpoint dell'API Microsoft Graph globale , sostituire questa stringa con https://graph.microsoft.com. Per gli endpoint delle distribuzioni di cloud nazionali, vedere Distribuzioni di cloud nazionali nella documentazione di Microsoft Graph.
  2. Sostituire il codice in src/app/profile/profile.component.ts per recuperare il profilo di un utente con una richiesta HTTP e sostituire con GRAPH_ENDPOINT l'endpoint di Microsoft Graph:

    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    const GRAPH_ENDPOINT = 'Enter_the_Graph_Endpoint_Here/v1.0/me';
    
    type ProfileType = {
      givenName?: string,
      surname?: string,
      userPrincipalName?: string,
      id?: string
    };
    
    @Component({
      selector: 'app-profile',
      templateUrl: './profile.component.html',
      styleUrls: ['./profile.component.css']
    })
    export class ProfileComponent implements OnInit {
      profile!: ProfileType;
    
      constructor(
        private http: HttpClient
      ) { }
    
      ngOnInit() {
        this.getProfile();
      }
    
      getProfile() {
        this.http.get(GRAPH_ENDPOINT)
          .subscribe(profile => {
            this.profile = profile;
          });
      }
    }
    
  3. Sostituire l'interfaccia utente in src/app/profile/profile.component.html per visualizzare le informazioni sul profilo:

    <div>
        <p><strong>First Name: </strong> {{profile?.givenName}}</p>
        <p><strong>Last Name: </strong> {{profile?.surname}}</p>
        <p><strong>Email: </strong> {{profile?.userPrincipalName}}</p>
        <p><strong>Id: </strong> {{profile?.id}}</p>
    </div>
    

Disconnettersi

  1. Aggiornare il codice in src/app/app.component.html per visualizzare in modo condizionale un Logout pulsante:

    <mat-toolbar color="primary">
      <a class="title" href="/">{{ title }}</a>
    
      <div class="toolbar-spacer"></div>
    
      <a mat-button [routerLink]="['profile']">Profile</a>
    
      <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
      <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button>
    
    </mat-toolbar>
    <div class="container">
      <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
      <router-outlet *ngIf="!isIframe"></router-outlet>
    </div>
    

Disconnettersi usando i reindirizzamenti

  1. Aggiornare il codice in src/app/app.component.ts per disconnettere un utente usando i reindirizzamenti:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
    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 {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
        } else {
          this.authService.loginRedirect();
        }
      }
    
      logout() { // Add log out function here
        this.authService.logoutRedirect({
          postLogoutRedirectUri: 'http://localhost:4200'
        });
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    

Disconnettersi con popup

  1. Aggiornare il codice in src/app/app.component.ts per disconnettere un utente usando popup:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, PopupRequest } from '@azure/msal-browser';
    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 {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginPopup({...this.msalGuardConfig.authRequest} as PopupRequest)
            .subscribe({
              next: (result) => {
                console.log(result);
                this.setLoginDisplay();
              },
              error: (error) => console.log(error)
            });
        } else {
          this.authService.loginPopup()
            .subscribe({
              next: (result) => {
                console.log(result);
                this.setLoginDisplay();
              },
              error: (error) => console.log(error)
            });
        }
      }
    
      logout() { // Add log out function here
        this.authService.logoutPopup({
          mainWindowRedirectUri: "/"
        });
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    

Eseguire test del codice

  1. Avviare il server Web in modo che rimanga in ascolto sulla porta eseguendo questi comandi da un prompt della riga di comando nella cartella dell'applicazione:

    npm install
    npm start
    
  2. Nel browser immettere http://localhost:4200e dovrebbe essere visualizzata una pagina simile alla seguente.

    Web browser displaying sign-in dialog

  3. Selezionare Accetta per concedere le autorizzazioni dell'app al profilo. Questa operazione si verifica la prima volta che si inizia ad accedere.

    Content dialog displayed in web browser

  4. Dopo il consenso, se si acconsente alle autorizzazioni richieste, l'applicazione Web visualizza una pagina di accesso riuscita.

    Results of a successful sign-in in the web browser

  5. Selezionare Profilo per visualizzare le informazioni sul profilo utente restituite nella risposta dalla chiamata all'API Microsoft Graph:

    Profile information from Microsoft Graph displayed in the browser

Aggiungere ambiti e autorizzazioni delegate

L'API Microsoft Graph richiede l'ambito User.Read per leggere il profilo di un utente. L'ambito User.Read viene aggiunto automaticamente a ogni registrazione dell'app. Altre API per Microsoft Graph e API personalizzate per il server back-end potrebbero richiedere altri ambiti. Ad esempio, l'API Microsoft Graph richiede l'ambito Mail.Read per visualizzare la posta elettronica dell'utente.

Quando si aggiungono ambiti, agli utenti potrebbe essere richiesto di fornire un consenso aggiuntivo per gli ambiti aggiunti.

Nota

Con l'aumentare del numero di ambiti è possibile che all'utente venga chiesto di esprimere anche altri tipi di consenso.

Assistenza e supporto

Se è necessaria assistenza, si vuole segnalare un problema o si vogliono ottenere informazioni sulle opzioni di supporto, vedere Assistenza e supporto per gli sviluppatori.

Passaggi successivi

  • Per altre informazioni, è possibile creare un'applicazione a pagina singola React che consente agli utenti di accedere alla serie di esercitazioni in più parti seguente.