Tutorial: Anmelden von Benutzern und Aufrufen der Microsoft Graph-API über eine Angular-Single-Page-Webanwendung (SPA) unter Verwendung des Autorisierungscodeflows

In diesem Tutorial erstellen Sie eine Angular-Single-Page-Webanwendung (SPA), die Benutzer*innen anmeldet und die Microsoft Graph-API über den Autorisierungscodeflow mit PKCE aufruft. Die von Ihnen erstellte SPA verwendet die Microsoft-Authentifizierungsbibliothek (Microsoft Authentication Library, MSAL) für Angular v2.

Dieses Tutorial umfasst folgende Punkte:

  • Registrieren der Anwendung im Microsoft Entra Admin Center
  • Erstellen eines Angular-Projekts mit npm
  • Hinzufügen von Code zur Unterstützung der Benutzeranmeldung und -abmeldung
  • Hinzufügen von Code zum Aufrufen der Microsoft Graph-API
  • Testen der App

MSAL Angular v2 bringt Verbesserungen gegenüber MSAL Angular v1 mit sich und unterstützt anstelle des impliziten Genehmigungsflows den Autorisierungscodeflow. Der implizite Flow wird von MSAL Angular v2 NICHT unterstützt.

Voraussetzungen

  • Node.js zum Ausführen eines lokalen Webservers
  • Visual Studio Code oder einen anderen Editor zum Bearbeiten von Projektdateien

Funktionsweise der Beispiel-App

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

Die in diesem Tutorial erstellte Beispiel-App ermöglicht einer Angular-SPA das Abfragen der Microsoft Graph-API oder einer Web-API, die von der Microsoft Identity Platform ausgegebene Token akzeptiert. Dabei wird die Microsoft Authentication Library (MSAL) für Angular v2 verwendet – ein Wrapper der MSAL.js v2-Bibliothek. Mit MSAL Angular können auf Angular 9+ basierende Anwendungen sowohl Unternehmensbenutzer über Microsoft Entra ID als auch Benutzer von Microsoft-Konten sowie Benutzer mit Social Media-Identität (z. B. Facebook, Google und LinkedIn) authentifizieren. Die Bibliothek ermöglicht es Anwendungen zudem, Zugriff auf Microsoft Cloud Services und Microsoft Graph zu erhalten.

In diesem Szenario wird nach der Benutzeranmeldung ein Zugriffstoken angefordert und den HTTP-Anforderungen über den Autorisierungsheader hinzugefügt. Die MSAL übernimmt die Tokenbeschaffung und -erneuerung.

Bibliotheken

In diesem Tutorial werden die folgenden Bibliotheken verwendet:

Bibliothek Beschreibung
MSAL Angular Microsoft Authentication Library für JavaScript: Angular-Wrapper
MSAL-Browser Microsoft Authentication Library für JavaScript v2: Browserpaket

Den Quellcode für sämtliche MSAL.js-Bibliotheken finden Sie auf GitHub im microsoft-authentication-library-for-js-Repository.

Abrufen des abgeschlossenen Codebeispiels

Möchten Sie stattdessen lieber das abgeschlossene Beispielprojekt für dieses Tutorial herunterladen? Klonen des ms-identity-javascript-angular-spa

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

Wenn Sie mit dem Tutorial fortfahren und die Anwendung selbst erstellen möchten, fahren Sie mit dem nächsten Abschnitt (Registrieren der Anwendungs- und Datensatzbezeichner) fort.

Registrieren der Anwendung und Notieren der Bezeichner

Tipp

Die Schritte in diesem Artikel können je nach dem Portal, mit dem Sie beginnen, geringfügig variieren.

Um die Registrierung abzuschließen, geben Sie einen Namen für Anwendung ein, geben Sie die unterstützten Kontotypen an, und fügen Sie einen Umleitungs-URI hinzu. Nach der Registrierung zeigt Bereich Übersicht der Anwendung die im Quellcode der Anwendung erforderlichen Bezeichner an.

  1. Melden Sie sich beim Microsoft Entra Admin Center mindestens mit der Rolle Anwendungsentwickler an.
  2. Wenn Sie Zugriff auf mehrere Mandanten haben, verwenden Sie das Symbol für Einstellungen im oberen Menü, um zum Mandanten zu wechseln, in dem Sie die Anwendung über das Menü Verzeichnisse + Abonnements registrieren möchten.
  3. Browsen Sie zu Identität>Anwendungen>App-Registrierungen.
  4. Wählen Sie Neue Registrierung aus.
  5. Geben Sie einen Namen für die Anwendung ein, z. B. Angular-SPA-auth-code.
  6. Wählen Sie für Unterstützte Kontotypen die Option Nur Konten in diesem Organisationsverzeichnis aus. Wenn Sie Informationen zu den verschiedenen Kontotypen wünschen, wählen Sie Entscheidungshilfe aus.
  7. Verwenden Sie unter Umleitungs-URI (optional) das Dropdownmenü, um Single-Page-Webanwendung (SPA) auszuwählen, und geben Sie http://localhost:4200 in das Textfeld ein.
  8. Wählen Sie Registrieren.
  9. Der Bereich Übersicht der Anwendung wird angezeigt, wenn die Registrierung abgeschlossen ist. Notieren Sie sich die Verzeichnis-ID (Mandant) und die Anwendungs-ID (Client), die im Quellcode Ihrer Anwendung verwendet werden sollen.

Erstellen Ihres Projekts

  1. Öffnen Sie Visual Studio Code, und wählen Sie Datei>Ordner öffnen... aus. Navigieren Sie zu dem Speicherort, an dem Ihr Projekt erstellt werden soll, und wählen Sie diesen aus.

  2. Öffnen Sie ein neues Terminal, indem Sie Terminal>Neues Terminal auswählen.

    1. Möglicherweise müssen Sie die Terminaltypen wechseln. Wählen Sie den Pfeil nach unten neben dem Symbol + im Terminal aus, und wählen Sie Eingabeaufforderung aus.
  3. Führen Sie die folgenden Befehle aus, um ein neues Angular-Projekt mit dem Namen msal-angular-tutorial zu erstellen, Angular Material-Komponentenbibliotheken, MSAL Browser und MSAL Angular zu installieren und Start- und Profilkomponenten zu generieren.

    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
    

Konfigurieren der Anwendung und Bearbeiten der Basisbenutzeroberfläche

  1. Öffnen Sie src/app/app.module.ts. MsalModule und MsalInterceptor müssen zusammen mit der Konstante isIE zu imports hinzugefügt werden. Sie fügen auch die Materialmodule hinzu. Ersetzen Sie den gesamten Inhalt der Datei durch folgenden Codeschnipsel:

    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. Ersetzen Sie die folgenden Werte durch die Werte, die Sie aus dem Microsoft Entra Admin Center erhalten haben. Weitere Informationen zu den verfügbaren konfigurierbaren Optionen finden Sie unter Initialisieren von Clientanwendungen.

    • clientId – Der Bezeichner der Anwendung, auch Client genannt. Ersetzen Sie Enter_the_Application_Id_Here durch den Wert Anwendungs-ID (Client), der zuvor auf der Übersichtsseite der registrierten Anwendung aufgezeichnet wurde.
    • authority – Diese besteht aus zwei Teilen:
      • Die Instanz ist der Endpunkt des Cloudanbieters. Geben Sie für die Azure-Hauptcloud oder für die globale Azure-Cloud https://login.microsoftonline.com ein. Überprüfen Sie die verschiedenen verfügbaren Endpunkte unter Nationale Clouds.
      • Die Mandanten-ID ist der Bezeichner des Mandanten, in dem die Anwendung registriert ist. Ersetzen Sie _Enter_the_Tenant_Info_Here durch den Wert Verzeichnis-ID (Mandant), der zuvor auf der Übersichtsseite der registrierten Anwendung aufgezeichnet wurde.
    • redirectUri: Der Ort, an den der Autorisierungsserver Benutzer*innen leitet, sobald die App erfolgreich autorisiert und ein Autorisierungscode oder Zugriffstoken zugewiesen wurde. Ersetzen Sie Enter_the_Redirect_Uri_Here durch http://localhost:4200.
  3. Öffnen Sie src/app/app-routing.module.ts, und fügen Sie Routen zu den Komponenten home und profile hinzu. Ersetzen Sie den gesamten Inhalt der Datei durch folgenden Codeschnipsel:

    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. Öffnen Sie src/app/app.component.html, und ersetzen Sie den Platzhaltercode durch Folgendes:

    <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. Öffnen Sie src/style.css, um die CSS-Formatierung zu definieren:

    @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. Öffnen Sie src/app/app.component.css, um der Anwendung CSS-Formatierung hinzuzufügen:

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

Anmelden mit Hilfe von Popups

  1. Öffnen Sie src/app/app.component.ts, und ersetzen Sie den Inhalt der Datei durch Folgendes, um Benutzer*innen über ein Popupfenster anzumelden:

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

Anmelden mittels Umleitungen

  1. Aktualisieren Sie src/app/app.module.ts, um ein Bootstrapping für MsalRedirectComponent durchzuführen. Dies ist eine dedizierte Umleitungskomponente zur Verarbeitung von Umleitungen. Ändern Sie Import MsalModule und Bootstrap AppComponent wie folgt:

    ...
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    ...
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    ...
    
  2. Öffnen Sie src/index.html, und ersetzen Sie den gesamten Inhalt der Datei durch den folgenden Codeschnipsel, der den Selektor <app-redirect> hinzufügt:

    <!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. Öffnen Sie src/app/app.component.ts, und ersetzen Sie den Code durch Folgendes, um Benutzer*innen unter Verwendung einer Vollbildumleitung anzumelden:

    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. Navigieren Sie zu src/app/home/home.component.ts, und ersetzen Sie den gesamten Inhalt der Datei durch den folgenden Codeschnipsel, um das Ereignis LOGIN_SUCCESS zu abonnieren:

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

Bedingtes Rendern

Wenn bestimmte Komponenten der Benutzeroberfläche nur für authentifizierte Benutzer*innen gerendert werden sollen, muss MsalBroadcastService für die Komponenten abonniert werden, um zu prüfen, ob Benutzer*innen angemeldet wurden und ob die Interaktion abgeschlossen ist.

  1. Fügen Sie MsalBroadcastService zu src/app/app.component.ts hinzu, und abonnieren Sie das observable-Element inProgress$, um vor dem Rendern der Benutzeroberfläche zu überprüfen, ob die Interaktion abgeschlossen und ein Konto angemeldet ist. Ihr Code sollte jetzt folgendermaßen aussehen:

    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. Aktualisieren Sie den Code in src/app/home/home.component.ts, um ebenfalls zu überprüfen, ob die Interaktion abgeschlossen ist, bevor die Benutzeroberfläche aktualisiert wird. Ihr Code sollte jetzt folgendermaßen aussehen:

    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. Ersetzen Sie den Code in src/app/home/home.component.html durch die folgenden bedingten Anzeigen:

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

Implementieren von Angular Guard

Mit der Klasse MsalGuard können Sie Routen schützen und vor dem Zugriff auf die geschützte Route eine Authentifizierung erzwingen. Mit den folgenden Schritten wird MsalGuard der Route Profile hinzugefügt. Der Schutz der Route Profile bedeutet Folgendes: Auch wenn sich Benutzer*innen nicht über die Schaltfläche Login anmelden, werden sie vor der Anzeige der Seite Profile von MsalGuard mittels Popup oder Umleitung zur Authentifizierung aufgefordert, wenn sie versuchen, auf die Route Profile zuzugreifen oder die Schaltfläche Profile auszuwählen.

MsalGuard ist eine Komfortklasse zur Verbesserung der Benutzerfreundlichkeit. Sie sollte jedoch nicht als Sicherheitsfeature verwendet werden. Bei Angriffen können clientseitige Wächter möglicherweise umgangen werden. Stellen Sie daher sicher, dass der Server keine Daten zurückgibt, auf die die Benutzer*innen keinen Zugriff haben sollen.

  1. Fügen Sie Ihrer Anwendung in src/app/app.module.ts die Klasse MsalGuard als Anbieter hinzu, und fügen Sie die Konfigurationen für MsalGuard hinzu. Bereiche, die später zum Abrufen von Token benötigt werden, können in authRequest angegeben werden, und die Art der Interaktion für den Wächter kann auf Redirect oder Popup festgelegt werden. Ihr Code sollte wie folgt aussehen:

    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. Legen Sie MsalGuard in src/app/app-routing.module.ts für die Routen fest, die Sie schützen möchten:

    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. Passen Sie die Anmeldeaufrufe in src/app/app.component.ts an, um den Satz authRequest in den Wächterkonfigurationen zu berücksichtigen. Ihr Code sollte nun wie folgt aussehen:

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

Abrufen eines Token

Interceptor von Angular

MSAL Angular verfügt über eine Interceptor-Klasse, mit der automatisch Token für ausgehende Anforderungen bezogen werden, bei denen der http-Client von Angular für bekannte geschützte Ressourcen verwendet wird.

  1. Fügen Sie Ihrer Anwendung in src/app/app.module.ts die Klasse Interceptor als Anbieter sowie die zugehörigen Konfigurationen hinzu. Ihr Code sollte nun wie folgt aussehen:

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

    Die geschützten Ressourcen werden als protectedResourceMap bereitgestellt. Bei den URLs, die Sie in der protectedResourceMap-Sammlung angeben, muss die Groß-/Kleinschreibung beachtet werden. Fügen Sie für jede Ressource Bereiche hinzu, die für die Rückgabe im Zugriffstoken angefordert werden.

    Beispiel:

    • ["user.read"] für Microsoft Graph
    • ["<Application ID URL>/scope"] für benutzerdefinierte Web-APIs (api://<Application ID>/access_as_user)

    Ändern Sie die Werte in protectedResourceMap wie im Anschluss beschrieben:

    • Enter_the_Graph_Endpoint_Here ist die Instanz der Microsoft Graph-API, mit der die Anwendung kommunizieren soll. Für den globalen Endpunkt der Microsoft Graph-API ersetzen Sie diese Zeichenfolge durch https://graph.microsoft.com. Informationen zu Endpunkten in nationalen Cloudbereitstellungen finden Sie in der Microsoft Graph Dokumentation unter Bereitstellungen nationaler Clouds.
  2. Ersetzen Sie den Code in src/app/profile/profile.component.ts, um ein Benutzerprofil mit einer HTTP-Anforderung abzurufen, und ersetzen Sie GRAPH_ENDPOINT durch den Microsoft Graph-Endpunkt:

    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. Ersetzen Sie die Benutzeroberfläche in src/app/profile/profile.component.html, um Profilinformationen anzuzeigen:

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

Abmelden

  1. Aktualisieren Sie den Code in src/app/app.component.html zur bedingten Anzeige einer Schaltfläche vom Typ 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>
    

Abmelden mittels Umleitungen

  1. Aktualisieren Sie den Code in src/app/app.component.ts, um einen Benutzer unter Verwendung von Umleitungen abzumelden:

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

Durch Pop-Ups abmelden

  1. Aktualisieren Sie den Code in src/app/app.component.ts, um einen Benutzer unter Verwendung von Popups abzumelden:

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

Testen Ihres Codes

  1. Starten Sie den Webserver, um an dem Port zu lauschen, indem Sie an einer Eingabeaufforderung im Anwendungsordner die folgenden Befehle ausführen:

    npm install
    npm start
    
  2. Geben Sie in Ihrem Browser http://localhost:4200 ein. Daraufhin sollte eine Seite wie folgende angezeigt werden.

    Web browser displaying sign-in dialog

  3. Wählen Sie Akzeptieren aus, um der App Berechtigungen für Ihr Profil zu erteilen. Dies geschieht, wenn Sie zum ersten Mal mit der Anmeldung beginnen.

    Content dialog displayed in web browser

  4. Nach der Einwilligung in die angeforderten Berechtigungen wird von der Webanwendung eine Seite für die erfolgreiche Anmeldung angezeigt.

    Results of a successful sign-in in the web browser

  5. Wählen Sie die Option Profil aus, um die Benutzerprofilinformationen anzuzeigen, die in der Antwort des Aufrufs der Microsoft Graph-API zurückgegeben werden:

    Profile information from Microsoft Graph displayed in the browser

Hinzufügen von Bereichen und delegierten Berechtigungen

Die Microsoft Graph-API benötigt den Bereich User.Read, um das Benutzerprofil zu lesen. Der Bereich User.Read wird automatisch jeder App-Registrierung hinzugefügt. Andere APIs für Microsoft Graph sowie benutzerdefinierte APIs für Ihren Back-End-Server erfordern möglicherweise andere Bereiche. Die Microsoft Graph-API benötigt beispielsweise den Bereich Mail.Read, um die E-Mail des Benutzers aufzuführen.

Wenn Sie Bereiche hinzufügen, werden Ihre Benutzer*innen möglicherweise aufgefordert, eine weitere Einwilligung für die hinzugefügten Bereiche zu erteilen.

Hinweis

Wenn Sie die Anzahl der Bereiche erhöhen, werden Benutzer ggf. zu weiteren Genehmigungen aufgefordert.

Hilfe und Support

Wenn Sie Hilfe benötigen, ein Problem melden möchten oder sich über Ihre Supportoptionen informieren möchten, finden Sie weitere Informationen unter Hilfe und Support für Entwickler.

Nächste Schritte

  • Erfahren Sie mehr, indem Sie in der folgenden mehrteiligen Tutorialreihe eine React-Single-Page-Webanwendung (SPA) für die Anmeldung von Benutzern erstellen.