Tutoriel : Connecter les utilisateurs et appeler l’API Microsoft Graph à partir d’une application monopage (SPA) Angular à l’aide du flux de code d’authentification
Dans ce tutoriel, vous générez une application monopage (SPA) Angular qui connecte les utilisateurs et appelle l’API Microsoft Graph à l’aide du flux de code d’autorisation avec la clé de preuve pour l’échange de code (PKCE). L’application SPA que vous générez utilise la bibliothèque d’authentification MSAL (Microsoft Authentication Library) pour Angular v2.
Dans ce tutoriel, vous allez :
- Enregistrez l'application dans le centre d'administration Microsoft Entra
- Créer un projet angulaire avec
npm
. - Ajouter du code pour prendre en charge la connexion et la déconnexion des utilisateurs
- Ajouter du code pour appeler l’API Microsoft Graph
- Test de l'application
MSAL Angular v2 utilise le flux de code d’autorisation avec PKCE dans le navigateur, ce qui constitue une amélioration par rapprt à MSAL Angular v1, qui utilisait un flux d’octroi implicite. Nous vous recommandons d’utiliser le flux de code d’autorisation avec PKCE pour les applications monopages (SPA), car il est plus sécurisé que le flux implicite. MSAL Angular v2 ne prend PAS en charge le flux implicite.
Prérequis
- Node.js pour l’exécution d’un serveur web local
- Visual Studio Code ou un autre éditeur pour la modification des fichiers projet
Fonctionnement de l’exemple d’application
L’exemple d’application créé dans ce tutoriel permet à une application monopage Angular d’interroger l’API Microsoft Graph ou une API web qui accepte des jetons émis par la plateforme d’identités Microsoft. Elle utilise la bibliothèque d’authentification MSAL (Microsoft Authentication Library) pour Angular v2, un wrapper de la bibliothèque MSAL.js v2. MSAL Angular permet aux applications Angular 9+ d’authentifier les utilisateurs en entreprise avec Microsoft Entra ID, ainsi que les utilisateurs qui ont des comptes Microsoft et des identités de réseaux sociaux tels que Facebook, Google et LinkedIn. La bibliothèque permet aussi aux applications d’accéder aux services cloud Microsoft et à Microsoft Graph.
Dans ce scénario, une fois qu’un utilisateur s’est connecté, un jeton d’accès est demandé et ajouté aux requêtes HTTP par le biais de l’en-tête d’autorisation. Les opérations d’acquisition et de renouvellement de jetons sont gérées par MSAL.
Bibliothèques
Ce tutoriel utilise les bibliothèques suivantes :
Bibliothèque | Description |
---|---|
MSAL Angular | Wrapper de la bibliothèque d’authentification Microsoft pour JavaScript Angular |
Navigateur MSAL | Package de navigateur de la bibliothèque d’authentification Microsoft pour JavaScript v2 |
Vous pouvez trouver le code source de toutes les bibliothèques MSAL.js dans le référentiel microsoft-authentication-library-for-js
sur GitHub.
Obtenir l’exemple de code complet
Préférez-vous télécharger l’exemple de projet terminé pour ce tutoriel à la place ? Cloner le paramètre ms-identity-javascript-angular-spa
git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git
Pour poursuivre le tutoriel et générer vous-même l’application, passez à la section suivante : Inscrire l’application et enregistrer les identificateurs.
Inscrire l’application et enregistrer les identificateurs
Conseil
Les étapes décrites dans cet article peuvent varier légèrement en fonction du portail de départ.
Pour terminer l’inscription, indiquez le nom de l’application, spécifiez les types de comptes pris en charge et ajoutez un URI de redirection. Une fois l’inscription terminée, le volet Vue d’ensemble de l’application affiche les identificateurs nécessaires dans le code source de l’application.
- Connectez-vous au centre d’administration de Microsoft Entra au minimum en tant que Développeur d’application.
- Si vous avez accès à plusieurs tenants, utilisez l’icône Paramètres dans le menu supérieur pour basculer vers le tenant dans lequel vous voulez inscrire l’application à partir du menu Répertoires + abonnements.
- Accédez à Identité>Applications>Inscriptions d’applications.
- Sélectionnez Nouvelle inscription.
- Entrez un Nom pour l’application, par exemple Angular-SPA-auth-code.
- Pour les Types de comptes pris en charge, sélectionnez Comptes dans cet annuaire organisationnel. Pour plus d’informations sur les différents types de comptes, sélectionnez l’option M’aider à choisir.
- Sous URI de redirection (facultatif), utilisez le menu déroulant pour sélectionner Application monopage (SPA), puis entrez
http://localhost:4200
dans la zone de texte. - Sélectionnez Inscription.
- Le Vue d’ensemble de l’application s’affiche une fois l’inscription terminée. Enregistrez l’ID du répertoire (locataire) et l’ID d’application (client) à utiliser dans le code source de votre application.
Créer votre projet
Ouvrez Visual Studio Code.
Sélectionnez Fichier>Ouvrir le dossier.... Accédez à et sélectionnez l’emplacement dans lequel vous souhaitez créer votre projet.
Ouvrez un nouveau terminal en sélectionnant Terminal>Nouveau Terminal.
- Vous devrez peut-être changer de type de terminal. Sélectionnez la flèche vers le bas à côté de l’icône + dans le terminal, puis sélectionnez Invite de commandes.
Exécutez les commandes suivantes pour créer un projet Angular nommé
msal-angular-tutorial
, installez les bibliothèques de composants Material Angular, MSAL Browser, MSAL Angular et générez des composants d’accueil et de profil.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
Configurer l’application et modifier l’interface utilisateur de base
Ouvrez src/app/app.module.ts. Les modules
MsalModule
etMsalInterceptor
doivent être ajoutés àimports
avec la constanteisIE
. Vous ajouterez également les modules matériels. Remplacez l’intégralité du contenu du fichier par l’extrait de code suivant :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 {}
Remplacez les valeurs suivantes par les valeurs obtenues à partir du centre d'administration Microsoft Entra. Pour plus d’informations sur les options configurables disponibles, consultez Initialiser les applications clientes.
clientId
– Identificateur de l’application, également appelé client. RemplacezEnter_the_Application_Id_Here
par la valeur du paramètre ID d’application (client) enregistrée précédemment depuis la page Vue d’ensemble de l’application inscrite.authority
– Il se compose de deux parties :- L’instance est le point de terminaison du fournisseur de cloud. Pour le cloud Azure principal ou mondial, entrez
https://login.microsoftonline.com
. Vérifiez les différents points de terminaison disponibles dans les clouds nationaux. - L’ID du locataire est l’identificateur du locataire dans lequel l’application est inscrite. Remplacez
_Enter_the_Tenant_Info_Here
par la valeur du paramètre ID d’application (locataire) enregistrée précédemment depuis la page Vue d’ensemble de l’application inscrite.
- L’instance est le point de terminaison du fournisseur de cloud. Pour le cloud Azure principal ou mondial, entrez
redirectUri
- Emplacement vers lequel le serveur d’autorisation envoie l’utilisateur une fois que l’application a été autorisée et qu’un code d’autorisation ou un jeton d’accès lui a été attribué. RemplacezEnter_the_Redirect_Uri_Here
parhttp://localhost:4200
.
Ouvrez src/app/app-routing.module.ts et ajoutez des itinéraires aux composants d’accueil et de profil. Remplacez l’intégralité du contenu du fichier par l’extrait de code suivant :
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 {}
Ouvrez src/app/app.component.html et remplacez le code existant par l’extrait de code suivant :
<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>
Ouvrez src/style.css pour définir le 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%; }
Ouvrez src/app/app.component.css pour ajouter un style CSS à l’application :
.toolbar-spacer { flex: 1 1 auto; } a.title { color: white; }
Se connecter à l’aide de fenêtres contextuelles
Ouvrez src/app/app.component.ts et remplacez le contenu du fichier par l’extrait de code suivant pour connecter un utilisateur à l’aide d’une fenêtre contextuelle :
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; } }
Se connecter à l’aide de redirections
Mettez à jour src/app/app.module.ts pour démarrer
MsalRedirectComponent
. Il s’agit d’un composant de redirection dédié qui gère les redirections. Modifiez l’importationMsalModule
et le démarrageAppComponent
pour qu’ils ressemblent à l’extrait de code suivant :... import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import ... bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here ...
Ouvrez src/index.html et remplacez l’intégralité du contenu du fichier par l’extrait de code suivant, qui ajoute le sélecteur
<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>
Ouvrez src/app/app.component.ts et remplacez le code par l’extrait de code suivant pour connecter un utilisateur à l’aide d’une redirection complète :
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; } }
Ouvrez src/app/home/home.component.ts et remplacez l’intégralité du contenu du fichier par l’extrait de code suivant pour vous abonner à l’événement
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); }); } }
Affichage conditionnel
Pour vous assurer que certains composants de l’interface utilisateur sont affichés uniquement pour les utilisateurs authentifiés, les composants doivent s’abonner à MsalBroadcastService pour vérifier si les utilisateurs sont connectés et si l’interaction est terminée.
Ajoutez
MsalBroadcastService
à src/app/app.component.ts, puis abonnez-vous auinProgress$
observable pour vérifier si l’interaction a eu lieu, et si un compte est connecté avant l’affichage de l’IU. Votre code doit désormais ressembler à ceci :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(); } }
Mettez à jour le code dans src/app/home/home.component.ts pour vérifier également si l’interaction a eu lieu avant la mise à jour de l’IU. Votre code doit désormais ressembler à ceci :
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; } }
Remplacez le code dans src/app/home/home.component.html par les affichages conditionnels suivants :
<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>
Implémenter Angular Guard
La classe MsalGuard
est l’une des classes que vous pouvez utiliser pour protéger des itinéraires et imposer une authentification avant tout accès à l’itinéraire protégé. Les étapes suivantes ajoutent MsalGuard
à l’itinéraire Profile
. Protéger l’itinéraire Profile
signifie que même si un utilisateur ne se connecte pas à l’aide du bouton Login
, s’il tente d’accéder à l’itinéraire Profile
ou s’il sélectionne le bouton Profile
, MsalGuard
l’invite à s’authentifier via une fenêtre contextuelle ou le redirige avant d’afficher la page Profile
.
MsalGuard
est une classe pratique que vous pouvez utiliser pour améliorer l’expérience utilisateur. Toutefois, son utilisation est déconseillée pour des raisons de sécurité. Des attaquants peuvent éventuellement contourner les protections côté client. Par conséquent, vous devez vous assurer que le serveur ne retourne aucune donnée à laquelle l’utilisateur ne doit pas accéder.
Ajoutez la classe
MsalGuard
en tant que fournisseur dans votre application au sein de src/app/app.module.ts, puis ajoutez les configurations pourMsalGuard
. Vous pouvez fournir dansauthRequest
les étendues nécessaires pour obtenir des jetons plus tard, et affecterRedirect
ouPopup
au type d’interaction de la protection. Votre code devrait ressembler à l’extrait de code suivant :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 {}
Définissez
MsalGuard
pour les routes à protéger dans 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 {}
Ajustez les appels de connexion dans src/app/app.component.ts pour prendre en compte le
authRequest
défini dans les configurations de protection. Votre code devrait maintenant ressembler à l’extrait de code suivant :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(); } }
Acquérir un jeton
Classe Interceptor d’Angular
MSAL Angular fournit une classe Interceptor
qui acquiert automatiquement des jetons pour les demandes sortantes qui utilisent le client http
Angular pour les ressources connues protégées.
Ajoutez la classe
Interceptor
en tant que fournisseur à votre application dans src/app/app.module.ts avec ses configurations. Votre code devrait maintenant ressembler à l’extrait de code suivant :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 {}
Les ressources protégées sont fournies en tant que
protectedResourceMap
. Les URL que vous fournissez dans la collectionprotectedResourceMap
sont sensibles à la casse. Pour chaque ressource, ajoutez les étendues à retourner dans le jeton d’accès.Par exemple :
["user.read"]
pour Microsoft Graph["<Application ID URL>/scope"]
pour les API web personnalisées (c’est-à-direapi://<Application ID>/access_as_user
)
Modifiez les valeurs de
protectedResourceMap
comme indiqué ici :Enter_the_Graph_Endpoint_Here
est l’instance de l’API Microsoft Graph avec laquelle l’application doit communiquer. Pour le point de terminaison de l’API Microsoft Graph global, remplacez cette chaîne parhttps://graph.microsoft.com
. Pour les points de terminaison dans les déploiements dans des clouds nationaux, consultez Déploiements dans des clouds nationaux dans la documentation Microsoft Graph.
Remplacez le code dans src/app/profile/profile.component.ts pour récupérer le profil d’un utilisateur par une requête HTTP, puis remplacez
GRAPH_ENDPOINT
par le point de terminaison 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; }); } }
Remplacez l’IU dans src/app/profile/profile.component.html pour afficher les informations de profil :
<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>
Se déconnecter
Mettez à jour le code dans src/app/app.component.html pour afficher un bouton
Logout
de manière conditionnelle :<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>
Se déconnecter à l’aide de redirections
Mettez à jour le code dans src/app/app.component.ts pour déconnecter un utilisateur à l’aide de redirections :
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(); } }
Se déconnecter à l’aide de fenêtres contextuelles
Mettez à jour le code dans src/app/app.component.ts pour déconnecter un utilisateur à l’aide de fenêtres contextuelles :
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(); } }
Test de votre code
Démarrez le serveur web pour écouter sur le port en exécutant les commandes suivantes sur une invite de ligne de commande à partir du dossier de l’application :
npm install npm start
Dans votre navigateur, entrez
http://localhost:4200
, et vous devriez voir une page qui ressemble à ce qui suit.Sélectionnez Accepter pour octroyer des autorisations d’application à votre profil. Cela se produit la première fois que vous commencez à vous connecter.
Si vous consentez aux autorisations demandées, l’application web affiche une page de connexion réussie :
Sélectionnez Profile pour afficher les informations de profil utilisateur retournées dans la réponse de l’appel à l’API Microsoft Graph :
Ajouter des étendues et des autorisations déléguées
L’API Microsoft Graph requiert l’étendue User.Read pour lire le profil d’un utilisateur. La portée User.Read est ajoutée automatiquement à chaque enregistrement d’application. D’autres API pour Microsoft Graph, ainsi que des API personnalisées pour votre serveur back-end, peuvent exiger d’autres étendues. Par exemple, l’API Microsoft Graph nécessite l’étendue Mail.Read afin de répertorier l’e-mail de l’utilisateur.
À mesure que vous ajoutez des étendues, vos utilisateurs peuvent être invités à donner un consentement supplémentaire pour les étendues ajoutées.
Notes
L’utilisateur peut être invité à donner des consentements supplémentaires à mesure que vous augmentez le nombre d’étendues.
Aide et support
Si vous avez besoin d’aide, si vous souhaitez signaler un problème ou si vous voulez en savoir plus sur vos options de support, consultez Aide et support pour les développeurs.
Étapes suivantes
- Découvrez plus d’informations en créant une application monopage (SPA) React qui connecte les utilisateurs dans cette série de tutoriels en plusieurs parties.