Zelfstudie: Gebruikers aanmelden en de Microsoft Graph-API aanroepen vanuit een Angular-app met één pagina met behulp van de verificatiecodestroom

In deze zelfstudie bouwt u een Angular-toepassing met één pagina die gebruikers aanmeldt en de Microsoft Graph API aanroept met behulp van de autorisatiecodestroom met PKCE. De SPA die u bouwt, maakt gebruik van de Microsoft Authentication Library (MSAL) voor Angular v2.

In deze zelfstudie:

  • De toepassing registreren in het Microsoft Entra-beheercentrum
  • Een Angular-project maken met npm
  • Code toevoegen voor de ondersteuning van het aan- en afmelden van gebruikers
  • Code toevoegen om de Microsoft Graph API aan te roepen
  • De app testen

MSAL Angular v2 verbetert MSAL Angular v1 door de verificatiecodestroom in de browser te ondersteunen in plaats van de impliciete toekenningsstroom. MSAL Angular v2 biedt GEEN ondersteuning voor de impliciete stroom.

Vereisten

  • Node.js- voor het uitvoeren van een lokale webserver.
  • Visual Studio Code of een andere editor voor het wijzigen van projectbestanden.

Hoe de voorbeeld-app werkt

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

Met de voorbeeldtoepassing die in deze zelfstudie wordt gemaakt, kan een Angular SPA een query uitvoeren bij de Microsoft Graph API of een web-API die tokens accepteert die zijn uitgegeven door het Microsoft-identiteitsplatform. Hierbij wordt de Microsoft Authentication Library (MSAL) voor Angular v2 gebruikt, een wrapper van de MSAL.js v2-bibliotheek. MET MSAL Angular kunnen Angular 9+-toepassingen zakelijke gebruikers verifiëren met behulp van Microsoft Entra-id, en ook gebruikers met Microsoft-accounts en sociale identiteiten, zoals Facebook, Google en LinkedIn. De bibliotheek zorgt er ook voor dat toepassingen toegang krijgen tot Microsoft-cloudservices en Microsoft Graph.

Wanneer in dit scenario een gebruiker zich aanmeldt, wordt er een toegangstoken gevraagd en toegevoegd aan HTTP-aanvragen via de autorisatie-header. Tokens worden opgehaald en verlengd door MSAL.

Bibliotheken

Deze zelfstudie maakt gebruik van de volgende bibliotheken:

Bibliotheek Beschrijving
MSAL Angular Micro Authentication Library voor JavaScript Angular Wrapper
MSAL Browser Microsoft Authentication Library voor JavaScript v2-browserpakket

U vindt de broncode voor alle MSAL.js bibliotheken in de microsoft-authentication-library-for-js opslagplaats op GitHub.

Het voltooide codevoorbeeld ophalen

Wilt u in plaats daarvan het voltooide voorbeeldproject voor deze zelfstudie downloaden? De ms-identity-javascript-angular-spa klonen

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

Als u wilt doorgaan met de zelfstudie en de toepassing zelf wilt bouwen, gaat u verder met de volgende sectie, De toepassing en record-id's registreren.

De toepassings- en record-id's registreren

Tip

Stappen in dit artikel kunnen enigszins variëren op basis van de portal waaruit u begint.

Als u de registratie wilt voltooien, geeft u de toepassing een naam op, geeft u de ondersteunde accounttypen op en voegt u een omleidings-URI toe. Nadat de toepassing is geregistreerd, worden in het deelvenster Overzicht van de toepassing de id's weergegeven die nodig zijn in de broncode van de toepassing.

  1. Meld u als toepassingsontwikkelaar aan bij het Microsoft Entra-beheercentrum.
  2. Als u toegang hebt tot meerdere tenants, gebruikt u het pictogram Instellingen in het bovenste menu om over te schakelen naar de tenant waarin u de toepassing wilt registreren in het menu Mappen en abonnementen.
  3. Blader naar identiteitstoepassingen>> App-registraties.
  4. Selecteer Nieuwe registratie.
  5. Voer een naam in voor de toepassing, zoals Angular-SPA-auth-code.
  6. Bij Ondersteunde accounttypen selecteert u Enkel accounts in deze organisatieadreslijst. Selecteer de optie Help mij kiezen voor informatie over verschillende accounttypen.
  7. Gebruik onder Omleidings-URI (optioneel) de vervolgkeuzelijst om toepassing met één pagina (SPA) te selecteren en voer http://localhost:4200 het tekstvak in.
  8. Selecteer Registreren.
  9. Het deelvenster Overzicht van de toepassing wordt weergegeven wanneer de registratie is voltooid. Noteer de map-id (tenant) en de toepassings-id (client) die moet worden gebruikt in de broncode van uw toepassing.

Uw project maken

  1. Open Visual Studio Code, selecteer Map>openen.... Navigeer naar en selecteer de locatie waar u uw project wilt maken.

  2. Open een nieuwe terminal door Terminal>New Terminal te selecteren.

    1. Mogelijk moet u van terminaltype wisselen. Selecteer de pijl-omlaag naast het + pictogram in de terminal en selecteer Opdrachtprompt.
  3. Voer de volgende opdrachten uit om een nieuw Angular-project te maken met de naam msal-angular-tutorial, installeer Angular Material-onderdeelbibliotheken, MSAL Browser, MSAL Angular en genereer basis- en profielonderdelen.

    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
    

De toepassing configureren en de basisgebruikersinterface bewerken

  1. Open src/app/app.module.ts. De MsalModule en MsalInterceptor moeten worden toegevoegd aan imports samen met de isIE constante. U voegt ook de materiaalmodules toe. Vervang de volledige inhoud van het bestand door het volgende codefragment:

    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. Vervang de volgende waarden door de waarden die zijn verkregen uit het Microsoft Entra-beheercentrum. Raadpleeg Clienttoepassingen initialiseren voor meer informatie over beschikbare opties die u kunt configureren.

    • clientId - De id van de toepassing, ook wel de client genoemd. Vervang Enter_the_Application_Id_Here door de waarde van de toepassings-id (client) die eerder is vastgelegd op de overzichtspagina van de geregistreerde toepassing.
    • authority - Dit bestaat uit twee delen:
      • Het exemplaar is het eindpunt van de cloudprovider. Voer https://login.microsoftonline.com in voor de primaire of algemene Azure-cloud. Neem contact op met de verschillende beschikbare eindpunten in nationale clouds.
      • De tenant-id is de id van de tenant waar de toepassing is geregistreerd. Vervang de _Enter_the_Tenant_Info_Here waarde van de map-id (tenant) die eerder is vastgelegd op de overzichtspagina van de geregistreerde toepassing.
    • redirectUri - de locatie waar de autorisatieserver de gebruiker verzendt zodra de app is geautoriseerd en een autorisatiecode of toegangstoken heeft verleend. Vervang Enter_the_Redirect_Uri_Here door http://localhost:4200.
  3. Open src/app/app-routing.module.ts en voeg routes toe aan de basis - en profielonderdelen . Vervang de volledige inhoud van het bestand door het volgende codefragment:

    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. Open src/app/app.component.html en vervang de bestaande code door het volgende:

    <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. Open src/style.css om de CSS te definiëren:

    @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. Open src/app/app.component.css om CSS-stijl toe te voegen aan de toepassing:

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

Aanmelden met pop-ups

  1. Open src/app/app.component.ts en vervang de inhoud van het bestand door het volgende om een gebruiker aan te melden met behulp van een pop-upvenster:

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

Aanmelden met omleidingen

  1. Werk src/app/app.module.ts bij om de MsalRedirectComponent te bootstrappen. Dit is een speciaal omleidingsonderdeel dat omleidingen verwerkt. Wijzig de MsalModule import en AppComponent bootstrap zodat deze er ongeveer als volgt uitziet:

    ...
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    ...
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    ...
    
  2. Open src/index.html en vervang de volledige inhoud van het bestand door het volgende fragment, waarmee de <app-redirect> selector wordt toegevoegd:

    <!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. Open src/app/app.component.ts en vervang de code door het volgende om een gebruiker aan te melden met behulp van een omleiding in een volledig 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. Navigeer naar src/app/home/home.component.ts en vervang de volledige inhoud van het bestand door het volgende codefragment om u te abonneren op de LOGIN_SUCCESS gebeurtenis:

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

Voorwaardelijke weergave

Als u bepaalde gebruikersinterface (UI) alleen wilt weergeven voor geverifieerde gebruikers, moeten onderdelen zich abonneren op de MsalBroadcastService gebruiker om te zien of gebruikers zijn aangemeld en de interactie is voltooid.

  1. Voeg de MsalBroadcastService toe aan src/app/app.component.ts en abonneer u op de waarneembare parameter inProgress$ om te controleren of de interactie is voltooid en of er een account is aangemeld voordat de gebruikersinterface wordt weergegeven. Uw code moet er nu als volgt uitzien:

    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. Werk de code in src/app/home/home.component.ts bij om ook te controleren of de interactie is voltooid voordat de gebruikersinterface wordt bijgewerkt. Uw code moet er nu als volgt uitzien:

    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. Vervang de code in src/app/home/home.component.html door de volgende voorwaardelijke weergaven:

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

Angular Guard implementeren

De MsalGuard klasse is een klasse die u kunt gebruiken om routes te beveiligen en verificatie te vereisen voordat u toegang krijgt tot de beveiligde route. Met de volgende stappen voegt u de MsalGuard route toe Profile . Het beveiligen van de Profile route betekent dat zelfs als een gebruiker zich niet aanmeldt met behulp van de Login knop, als hij de Profile route probeert te openen of de Profile knop selecteert, de MsalGuard gebruiker wordt gevraagd om zich te verifiëren via pop-up of omleiding voordat de Profile pagina wordt weergegeven.

MsalGuard is een handige klasse die u kunt gebruiken om de gebruikerservaring te verbeteren, maar deze moet niet worden vertrouwd voor beveiliging. Aanvallers kunnen mogelijk beveiliging aan de clientzijde omzeilen en u moet ervoor zorgen dat de server geen gegevens retourneert waartoe de gebruiker geen toegang mag krijgen.

  1. Voeg de klasse MsalGuard toe als provider in uw toepassing in src/app/app.module.ts en voeg de configuraties voor de MsalGuard toe. Bereiken die nodig zijn om later tokens te verkrijgen, kunnen worden opgegeven in de authRequest, en het type interactie voor de beveiliging kan worden ingesteld op Redirect of Popup. Uw code moet er als volgt uitzien:

    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. Stel de MsalGuard in voor de routes die u wilt beveiligen 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. Pas de aanmeldingsaanroepen in src/app/app.component.ts aan om rekening te houden met de authRequest die is ingesteld in de beveiligingsconfiguraties. Uw code moet er nu als volgt uitzien:

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

Een token verkrijgen

Angular-interceptor

MSAL Angular biedt de klasse Interceptor die automatisch tokens ophaalt voor uitgaande aanvragen naar bekende beveiligde bronnen en die gebruikmaken van de Angular http-client.

  1. Voeg de klasse Interceptor als provider toe aan uw toepassing in src/app/app.module.ts, met de bijbehorende configuraties. Uw code moet er nu als volgt uitzien:

    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 {}
    

    De beveiligde resources worden geleverd als een protectedResourceMap. De URL's die u opgeeft in de verzameling protectedResourceMap zijn hoofdlettergevoelig. Voeg voor elke resource bereiken toe die worden aangevraagd om te worden geretourneerd in het toegangstoken.

    Voorbeeld:

    • ["user.read"] voor Microsoft Graph
    • ["<Application ID URL>/scope"] voor aangepaste web-API's (api://<Application ID>/access_as_user)

    Wijzig de waarden in de protectedResourceMap, zoals hier wordt beschreven:

    • Enter_the_Graph_Endpoint_Here is het exemplaar van de Microsoft Graph API waarmee de toepassing moet communiceren. Voor het globale Microsoft Graph API-eindpunt vervangt u deze tekenreeks door https://graph.microsoft.com. Zie Nationale cloudimplementaties in de Microsoft Graph-documentatie voor eindpunten in nationale cloudimplementaties.
  2. Vervang de code in src/app/profile/profile.component.ts om het profiel van een gebruiker op te halen door een HTTP-aanvraag en vervang de GRAPH_ENDPOINT code door het Microsoft Graph-eindpunt:

    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. Vervang de gebruikersinterface in src/app/profile/profile.component.html om profielgegevens weer te geven:

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

Afmelden

  1. Werk de code in src/app/app.component.html bij om voorwaardelijk een knop Logout weer te geven:

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

Afmelden met omleidingen

  1. Werk de code in src/app/app.component.ts bij om een gebruiker af te melden met behulp van omleidingen:

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

Afmelden met pop-ups

  1. Werk de code in src/app/app.component.ts bij om een gebruiker af te melden met behulp van pop-ups:

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

Uw code testen

  1. Start de webserver om te luisteren naar de poort door de volgende opdrachten in een opdrachtregelprompt in de toepassingsmap uit te voeren:

    npm install
    npm start
    
  2. Voer in uw browser een http://localhost:4200pagina in die er ongeveer als volgt uitziet.

    Web browser displaying sign-in dialog

  3. Selecteer Accepteren om de app-machtigingen toe te kennen aan uw profiel. Dit gebeurt de eerste keer dat u zich begint aan te melden.

    Content dialog displayed in web browser

  4. Nadat u toestemming hebt gegeven, wordt in de webtoepassing een geslaagde aanmeldingspagina weergegeven als u toestemming geeft voor de aangevraagde machtigingen.

    Results of a successful sign-in in the web browser

  5. Selecteer Profiel om de gebruikersprofielgegevens weer te geven die worden geretourneerd in het antwoord van de aanroep naar de Microsoft Graph API:

    Profile information from Microsoft Graph displayed in the browser

Bereiken en gedelegeerde toestemmingen toevoegen

De Microsoft Graph-API vereist het bereik User.Read om het profiel van een gebruiker te lezen. Het bereik User.Read wordt automatisch toegevoegd aan elke app-registratie. Voor andere API's voor Microsoft Graph en aangepaste API's voor uw back-endserver zijn mogelijk andere bereiken vereist. De Microsoft Graph API vereist bijvoorbeeld het bereik Mail.Read om de e-mail van de gebruiker op te sommen.

Wanneer u bereiken toevoegt, wordt uw gebruikers mogelijk gevraagd om extra toestemming te geven voor de toegevoegde bereiken.

Notitie

De gebruiker wordt mogelijk gevraagd om aanvullende machtigingen te geven naarmate u het aantal bereiken verhoogt.

Help en ondersteuning

Als u hulp nodig hebt, een probleem wilt melden of meer informatie wilt over uw ondersteuningsopties, raadpleegt u Hulp en ondersteuning voor ontwikkelaars.

Volgende stappen

  • Meer informatie vindt u door een React-toepassing met één pagina (SPA) te bouwen waarmee gebruikers worden aangemeld in de volgende reeks meerdelige zelfstudies.