Esercitazione: Eseguire l’accesso degli utenti e chiamare l'API Microsoft Graph da un'applicazione a pagina singola Angular usando il flusso del codice di autenticazione
In questa esercitazione viene creata un'applicazione a pagina singola Angular che accede agli utenti e chiama l'API Microsoft Graph usando il flusso del codice di autorizzazione con la chiave di prova per Exchange (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 usa il flusso del codice di autorizzazione con PKCE nel browser, migliorando in MSAL Angular v1, che ha usato il flusso di concessione implicita. Si consiglia di usare il flusso del codice di autorizzazione con PKCE per applicazioni a pagina singola perché è più sicuro del flusso implicito. 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
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 della versione 9 e successive di autenticare gli utenti aziendali usando Microsoft Entra ID, nonché gli utenti con account Microsoft e con identità basate su social network 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. MSAL gestisce l'acquisizione e il rinnovo dei token.
Librerie
Questa esercitazione usa le librerie seguenti:
Libreria | Descrizione |
---|---|
MSAL Angular | Microsoft Authentication Library per il wrapper Angular JavaScript |
MSAL Browser | Pacchetto del browser di Microsoft Authentication Library per JavaScript 2.0 |
È possibile trovare il codice sorgente per tutte le librerie MSAL.js nel repository microsoft-authentication-library-for-js
su GitHub.
Ottenere l'esempio di codice completo
Si preferisce scaricare 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 registrare gli identificatori.
Registrare l'applicazione e gli identificatori del record
Suggerimento
I passaggi descritti in questo articolo possono variare leggermente in base al portale di partenza.
Per completare la registrazione indicare il nome dell'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.
- Accedere all' Interfaccia di amministrazione di Microsoft Entra almeno come Sviluppatore di applicazioni.
- 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.
- Passare a Identità>Applicazioni>Registrazioni app.
- Seleziona Nuova registrazione.
- Immettere un Nome per l'applicazione, ad esempio Angular-SPA-auth-code.
- Per Tipi di account supportati selezionare Account solo in questa directory dell'organizzazione. Per informazioni sui diversi tipi di account selezionare l'opzione Suggerimenti per la scelta.
- In URI di reindirizzamento (facoltativo) usare il menu a discesa per selezionare Applicazione a pagina singola e immettere
http://localhost:4200
nella casella di testo. - Selezionare Registra.
- 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
Aprire Visual Studio Code.
Selezionare File>Apri cartella.... Passare a e selezionare il percorso in cui creare il progetto.
Aprire un nuovo terminale selezionando Terminale>Nuovo terminale.
- Potrebbe essere necessario cambiare i tipi di terminale. Selezionare la freccia giù accanto all'icona + nel terminale e selezionare Prompt dei comandi.
Eseguire i comandi seguenti per creare un nuovo progetto Angular denominato
msal-angular-tutorial
, installare le librerie dei componenti di Angular Material, MSAL Browser, MSAL Angular e generare i 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
Aprire src/app/app.module.ts.
MsalModule
eMsalInterceptor
devono essere aggiunti aimports
insieme alla costanteisIE
. Si aggiungono anche i moduli di materiale. 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 {}
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. SostituireEnter_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 in Cloud nazionali. - L' ID tenant è l'identificatore del tenant dove è registrata l'applicazione. Sostituire il valore
_Enter_the_Tenant_Info_Here
con il valore ID directory (tenant) registrato in precedenza dalla pagina di panoramica dell'applicazione registrata.
- L’ Istanza è l'endpoint del provider di servizi cloud. Per il cloud globale o principale di Azure, immettere
redirectUri
: il percorso a 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. SostituisciEnter_the_Redirect_Uri_Here
conhttp://localhost:4200
.
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 {}
Aprire src/app/app.component.html e sostituire il codice esistente con il frammento di 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>
Aprire src/style.css per definire gli stili 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%; }
Aprire src/app/app.component.css per aggiungere stili CSS all'applicazione:
.toolbar-spacer { flex: 1 1 auto; } a.title { color: white; }
Accesso tramite popup
Aprire src/app/app.component.ts e sostituire il contenuto del file con il frammento di codice 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; } }
Accesso tramite reindirizzamenti
Aggiornare src/app/app.module.ts per avviare
MsalRedirectComponent
. Si tratta di un componente di reindirizzamento dedicato che gestisce i reindirizzamenti. Modificare l'importazioneMsalModule
eAppComponent
il bootstrap in modo che sia simile al frammento di codice seguente:... import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import ... bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here ...
Aprire src/index.html e sostituire l'intero contenuto del file con il frammento di codice seguente e che aggiunge il selettore
<app-redirect>
:<!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>
Aprire src/app/app.component.ts e sostituire il codice con il frammento di 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; } }
Aprire 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 assicurarsi che determinati componenti dell'interfaccia utente vengano visualizzati solo per gli utenti autenticati, i componenti devono sottoscrivere MsalBroadcastService per verificare se gli utenti hanno eseguito l'accesso e se l'interazione è stata completata.
Aggiungere
MsalBroadcastService
a src/app/app.component.ts e sottoscrivere la funzione osservabileinProgress$
per verificare se l'interazione viene completata e se un account viene registrato 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(); } }
Aggiornare il codice in src/app/home/home.component.ts per verificare anche che l'interazione venga 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; } }
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 classe MsalGuard
è una che è possibile usare per proteggere le route e richiedere l'autenticazione prima di accedere alla route protetta. Nei passaggi seguenti viene aggiunta la route MsalGuard
alla route Profile
. La protezione della route Profile
significa che anche se un utente non esegue l'accesso usando il pulsante Login
, se tenta di accedere alla route Profile
o se seleziona il pulsante Profile
, il pulsante MsalGuard
richiede all'utente di eseguire l'autenticazione tramite popup o reindirizzamento prima di mostrare la paginaProfile
.
MsalGuard
è una classe pratica che è possibile usare per migliorare l'esperienza utente, ma non deve essere basata sulla sicurezza. Gli utenti malintenzionati possono potenzialmente aggirare i controlli lato client; assicurarsi quindi che il server non restituisca dati a cui l'utente non deve accedere.
Aggiungere la classe
MsalGuard
come provider all'applicazione in src/app/app.module.tse aggiungere le configurazioni perMsalGuard
. Gli ambiti necessari per acquisire i token in un secondo momento possono essere forniti inauthRequest
e il tipo di interazione per Guard può essere impostato suRedirect
oPopup
. Il codice dovrebbe essere simile al 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, 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 {}
Impostare
MsalGuard
nelle 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 {}
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 frammento di codice 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.
Aggiungere la classe
Interceptor
come provider all'applicazione in src/app/app.module.ts, con le relative configurazioni. Il codice dovrebbe ora essere simile al frammento di codice 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 raccoltaprotectedResourceMap
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, ovveroapi://<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 conhttps://graph.microsoft.com
. Per gli endpoint delle distribuzioni di cloud nazionali, vedere Distribuzioni di cloud nazionali nella documentazione di Microsoft Graph.
Sostituire il codice in src/app/profile/profile.component.ts per recuperare il profilo utente con richiesta HTTP e sostituire
GRAPH_ENDPOINT
con 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; }); } }
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
Aggiornare il codice in src/app/app.component.html per visualizzare in modo condizionale un pulsante
Logout
:<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>
Disconnessione tramite reindirizzamenti
Aggiornare il codice in src/app/app.component.ts per disconnettere un utente tramite 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(); } }
Disconnessione tramite popup
Aggiornare il codice in src/app/app.component.ts per disconnettere un utente tramite 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
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
Nel browser immettere
http://localhost:4200
e verrà visualizzata una pagina simile alla seguente.Selezionare Accetta per concedere le autorizzazioni dell'app al profilo. Ciò si verifica la prima volta che si inizia ad accedere.
Se si acconsente alle autorizzazioni richieste, l'applicazione Web visualizza una pagina di accesso riuscita.
Selezionare Profilo per visualizzare le informazioni del profilo utente restituite in risposta alla chiamata all'API Microsoft Graph:
Aggiungere ambiti e autorizzazioni delegate
L'API Microsoft Graphs 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 le API personalizzate per il server back-end possono richiedere anche altri ambiti. Ad esempio, l'API Microsoft Graphs richiede l'ambito Mail.Read per elencare il messaggio di posta elettronica dell'utente.
Aggiungendo altri ambiti, agli utenti può 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 ulteriori informazioni, creare un'applicazione a pagina singola (SPA) che effettua l'accesso degli utenti nella seguente serie di esercitazioni in più parti.