Schreiben einer einseitigen JavaScript-App zum Abrufen von Outlook-Mail, -Kalender und -Kontakten

In diesem Leitfaden werden Sie schrittweise durch den Prozess des Erstellens einer einfachen einer einseitigen JavaScript-App zum Abrufen von Nachrichten in Office 365 oder Outlook.com geführt. Wenn Sie die hier beschriebenen Schritte ausführen, sollte der Quellcode in diesem Repository das Ergebnis sein.

In diesem Leitfaden wird Microsoft Graph zum Zugriff auf Outlook-Mail verwendet. Microsoft empfiehlt die Verwendung von Microsoft Graph für den Zugriff auf Outlook-Mail, -Kalender und -Kontakte. Verwenden Sie die Outlook-APIs nur dann direkt (über https://outlook.office.com/api), wenn Sie ein Feature benötigen, das in den Graph-Endpunkten nicht verfügbar ist. Eine Version dieses Beispiels mit Verwendung der Outlook-APIs finden Sie in dieser Verzweigung.

Hinweis

In diesem Leitfaden wird http-server verwendet, um einen einfachen Befehlszeilen-HTTP-Server für Entwicklungszwecke bereitzustellen. Das http-server-Paket erfordert Node.js und NPM für die Installation und Ausführung. Eine Verwendung von http-server ist jedoch nicht erforderlich, Sie können für dieses Lernprogramm einen beliebigen HTTP-Server verwenden.

Erstellen der App

Erstellen Sie ein leeres Verzeichnis, in dem Sie die App erstellen möchten. Für die Zwecke dieses Leitfadens wird davon ausgegangen, dass der Name des Verzeichnisses javascript-tutorial ist, Sie können aber gerne einen beliebigen Namen verwenden.

Da unsere App clientseitig und JavaScript-basiert ist, benötigen wir einen HTTP-Server für unsere HTML- und JavaScript-Dateien. Für diesen Leitfaden können Sie gerne einen von Ihnen bevorzugten HTTP-Server verwenden. Zur Vereinfachung enthält dieses Handbuch Schritte zur Verwendung von http-server, um schnell einen Entwicklungswebserver in der Befehlszeile zu erstellen.

Einrichten des Webservers

Bevor Sie beginnen, müssen Sie Node.js installiert haben.

  1. Öffnen Sie eine Eingabeaufforderung, und legen Sie das aktuelle Verzeichnis auf das javascript-tutorial-Verzeichnis fest.

  2. Geben Sie den folgenden Befehl zur Installation von http-server ein:

    npm install http-server -g
    
  3. Geben Sie den folgenden Befehl ein, um den Server zu starten:

    http-server
    
  4. Öffnen Sie einen Browser, und navigieren Sie zu http://localhost:8080. Wenn der Server ordnungsgemäß funktioniert, wird in etwa Folgendes angezeigt:

    Browser, in dem eine Verzeichnisauflistung eines leeren Verzeichnisses angezeigt wird.

Dadurch wird bestätigt, dass der Server funktioniert. Nun können wir also mit dem Programmieren beginnen. Lassen Sie den Server ausgeführt. Wenn Sie ihn zu einem bestimmten Zeitpunkt beenden, starten Sie ihn unter Verwendung des http-server-Befehls im javascript-tutorial-Verzeichnis neu.

Entwerfen der App

Unsere App ist sehr einfach. Wenn ein Benutzer die Website besucht, sieht er einen Link zum Anmelden und Anzeigen seiner E-Mails. Beim Klicken auf den Link gelangt er zur Azure-Anmeldeseite, wo er sich mit seinem Office 365- oder Outlook.com-Konto anmelden und unserer App Zugriff gewähren kann. Schließlich wird er zurück zu unserer App geleitet, die eine Liste der neuesten E-Mails im Posteingang des Benutzers anzeigt.

Beginnen wir, indem wir eine HTML-Seite zu unserer App hinzufügen. Erstellen Sie mit Ihrem bevorzugten Editor eine Datei namens index.html im Verzeichnis javascript-tutorial, und fügen Sie den folgenden Code hinzu.

Inhalte der Datei index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Outlook SPA Demo</title>
  <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
  <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet">
  <link href="style.css" rel="stylesheet" type="text/css" />

  <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.min.js"></script>
  <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"></script>
  <script src="//kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
  <script src="graph-js-sdk-web.js"></script>
  <script src="outlook-demo.js"></script>
</head>

<body>
  <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Outlook SPA Demo</a>
      </div>
      <div id="navbar" class="navbar-collapse collapse">
        <ul class="nav navbar-nav authed-nav">
          <li id='home-nav'><a href="#">Home</a></li>
          <li id='inbox-nav'><a href="#inbox">Inbox</a></li>
        </ul>
        <ul class="nav navbar-nav navbar-right authed-nav">
          <li><a href="#signout">Sign out</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container main-container">
    <div id="signin-prompt" class="jumbotron page">
      <h1>Outlook SPA Demo</h1>
      <p>This example shows how to get an OAuth token from Azure using the <a href="https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/">implicit grant flow</a> and to use that token to make calls to the Outlook APIs.</p>
      <p>
        <a class="btn btn-lg btn-primary" href="#" role="button" id="connect-button">Connect to Outlook</a>
      </p>
    </div>
    <!-- logged in user welcome -->

    <!-- unsupported browser message -->

    <!-- error message -->

    <!-- inbox display -->

    <!-- token display -->

  </div>
</body>
</html>

Speichern Sie die Datei, und aktualisieren Sie Ihren Browser. Sie sollten jetzt die sehr einfache Startseite für die App sehen. Hier eine Übersicht der Funktionsweise des Codes:

  • Fügt Bootstrap für Format und die Navigationsleiste hinzu.
  • Fügt jQuery für die Bearbeitung der Seite hinzu.
  • Fügt die jsrsasign-Bibliothek für die Tokenvalidierung hinzu (dazu später mehr).
  • Fügt Handlebars für einfache Vorlagen hinzu.
  • Fügt die Microsoft Graph-JavaScript-Clientbibliothek für den Zugriff auf Outlook-Mail hinzu.
  • Fügt die lokalen Dateien style.css und outlook-demo.js hinzu.

Die Arbeitsweise der Seite basiert auf den Hashwerten in den HREFs auf den Schaltflächen in der Seite. Durch Klicken auf diese Schaltflächen wird keine neue Seite geladen. Stattdessen wird JavaScript-Code aufgerufen, um Daten zu laden und die Seite zu aktualisieren.

Erstellen Sie eine neue Datei mit dem Namen style.css im Verzeichnis javascript-tutorial. Fügen Sie den folgenden Code zu der Datei hinzu, und speichern Sie sie.

Inhalte der Datei style.css

body {
  padding-top: 70px;
}

Erstellen Sie schließlich eine neue Datei mit dem Namen outlook-demo.js im Verzeichnis javascript-tutorial. Fügen Sie den folgenden Code zu der Datei hinzu, und speichern Sie sie.

Inhalte der Datei outlook-demo.js

$(function() {

  // Check for browser support for sessionStorage
  if (typeof(Storage) === 'undefined') {
    render('#unsupportedbrowser');
    return;
  }

  // Check for browser support for crypto.getRandomValues
  var cryptObj = window.crypto || window.msCrypto; // For IE11
  if (cryptObj === undefined || cryptObj.getRandomValues === 'undefined') {
    render('#unsupportedbrowser');
    return;
  }

  render(window.location.hash);

  $(window).on('hashchange', function() {
    render(window.location.hash);
  });

  function render(hash) {

    var action = hash.split('=')[0];

    // Hide everything
    $('.main-container .page').hide();

    var isAuthenticated = false;

    var pagemap = {

      // Welcome page
      '': function() {
        renderWelcome(isAuthenticated);
      },

      // Receive access token

      // Signout

      // Error display

      // Display inbox

      // Shown if browser doesn't support session storage
      '#unsupportedbrowser': function () {
        $('#unsupported').show();
      }
    }

    if (pagemap[action]){
      pagemap[action]();
    } else {
      // Redirect to home page
      window.location.hash = '#';
    }
  }

  function setActiveNav(navId) {
    $('#navbar').find('li').removeClass('active');
    $(navId).addClass('active');
  }

  function renderWelcome(isAuthed) {
    if (isAuthed) {
      $('#username').text(sessionStorage.userDisplayName);
      $('#logged-in-welcome').show();
      setActiveNav('#home-nav');
    } else {
      $('#connect-button').attr('href', buildAuthUrl());
      $('#signin-prompt').show();
    }
  }

  // OAUTH FUNCTIONS =============================

  // OUTLOOK API FUNCTIONS =======================

  // HELPER FUNCTIONS ============================

});

Dieser Code erstellt eine render-Methode, die Teile der Seite basierend auf dem aktuellen #-Wert in der URL selektiv anzeigt. Für jeden unterstützten Hashwert gibt es einen entsprechenden Eintrag im pagemap-Objekt. Derzeit gibt es für die Startseite den leeren Wert und einen neuen Wert #unsupportedbrowser. Das allererste, was der Code macht, ist nach Unterstützung der Storage-Benutzeroberfläche und der crypto.getRandomValues-Funktion zu suchen. Die App verwendet die Webspeicher-API zum Speichern von Zugriffstoken und die crypto.getRandomValues-Funktion zum Generieren von Nonce-Werten, wir müssen daher sicherstellen, dass der Browser diese unterstützt (was bei den meisten modernen Browsern der Fall ist).

Vorausgesetzt, Ihr Browser unterstützt Webspeicher und crypto.getRandomValues, wird durch Aktualisieren an diesem Punkt jetzt die Startseite wie zuvor angezeigt. Wenn Sie die URL im Browser in http://localhost:8080/#unsupportedbrowser ändern, sollte die Navigationsleiste oben angezeigt werden, der Rest der Seite ist aber leer. Wir fügen nun eine Notiz ein, um dem Benutzer mitzuteilen, was passiert ist.

Öffnen Sie index.html, und suchen Sie die folgende Zeile:

<!-- unsupported browser message -->

Fügen Sie unmittelbar nach dieser Zeile den folgenden Code hinzu:

Neuer Code in index.html

<div id="unsupported" class="jumbotron page">
  <h1>Oops....</h1>
  <p>This page requires browser support for <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">session storage</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues"><code>crypto.getRandomValues</code></a>. Unfortunately, your browser does not support one or both features. Please visit this page using a different browser.</p>
</div>

Speichern Sie die Änderung, und navigieren Sie zu http://localhost:8080/#unsupportedbrowser. Nun sollte die Meldung zum nicht unterstützten Browser angezeigt werden.

Registrieren der App

Wichtig

Neue App-Registrierungen sollten im Anwendungsregistrierungsportal erstellt und verwaltet werden, damit sie mit Outlook.com kompatibel sind. Erstellen Sie neue App-Registrierungen nur dann im Azure-Verwaltungsportal Folgendes auf Ihre App zutrifft:

  • Sie verwendet den OAuth2 Client Credentials Grant-Fluss oder
  • Sie muss neben Outlook auf andere Office 365-Arbeitslasten zugreifen (z. B. OneDrive for Business oder SharePoint)

Beachten Sie, dass mithilfe des Azure-Verwaltungsportals registrierte Apps nicht mit Outlook.com kompatibel sind und dass Berechtigungsbereiche nicht dynamisch angefordert werden können. Vorhandene App-Registrierungen, die im Azure-Verwaltungsportal erstellt wurden, funktionieren weiterhin nur für Office 365. Diese Registrierungen werden nicht im Anwendungsregistrierungsportal angezeigt und müssen im Azure-Verwaltungsportal verwaltet werden.

Kontoanforderungen

Um das Anwendungsregistrierungsportal zu verwenden, benötigen Sie entweder ein Office 365-Geschäfts- oder Schulkonto oder ein Microsoft-Konto. Wenn Sie nicht über eines dieser Konten verfügen, haben Sie verschiedene Möglichkeiten:

  • Registrieren Sie sich hier für ein neues Microsoft-Konto.
  • Sie haben mehrere Möglichkeiten, ein Office 365-Abonnement zu erhalten:

REST-API-Verfügbarkeit

Die REST-API ist derzeit auf allen Office 365-Konten, die über Exchange Online verfügen, sowie auf allen Outlook.com-Konten aktiviert.

Wechseln Sie zum App-Registrierungsportal, um schnell eine App-ID zu erhalten.

  1. Melden Sie sich über den Link Anmelden mit Ihrem Microsoft-Konto (Outlook.com) oder Ihrem Geschäfts-, Schul- oder Unikonto (Office 365) an.
  2. Klicken Sie auf die Schaltfläche App hinzufügen. Geben Sie javascript-tutorial für den Namen ein, und klicken Sie auf Anwendung erstellen.
  3. Suchen Sie den Abschnitt Plattformen, und klicken Sie auf Plattform hinzufügen. Wählen Sie Web aus, und geben Sie dann http://localhost:8080 unter Umleitungs-URIs ein. Stellen Sie sicher, dass die Option Impliziten Fluss zulassen aktiviert ist.
  4. Klicken Sie auf Speichern, um die Registrierung abzuschließen. Kopieren Sie die Anwendungs-ID, und speichern Sie sie. Wir benötigen sie bald.

So sollten die Details Ihrer App-Registrierung aussehen, wenn Sie fertig sind.

Screenshot der abgeschlossenen App-Registrierung im App-Registrierungsportal

Implementieren von OAuth2

Unser Ziel in diesem Abschnitt besteht darin, den Fluss einer impliziten OAuth2-Genehmigung mit Azure AD über den Link auf unserer Homepage zu initiieren. Dieser Fluss wurde für SPA-Apps entworfen und ermöglicht es uns, ein Zugriffstoken abzurufen, ohne hierfür einen geheimen Clientschlüssel auszutauschen oder zusätzliche Anforderungen an einen Endpunkt vorzunehmen, der Token ausgibt.

Der implizite Fluss ist ziemlich einfach. Der Benutzer navigiert zu einem von Azure gehosteten Authentifizierungslink, bei dem er sich anmeldet und Zugriff auf unsere App gewährt. In Azure erfolgt die Umleitung zu der App mit dem Ergebnis. Wenn sich der Benutzer erfolgreich angemeldet und seine Zustimmung abgegeben hat, enthält das Ergebnis ein Zugriffstoken und ein ID-Token.

Der erste Schritt besteht darin, den Authentifizierungslink zu generieren. Öffnen Sie die Datei outlook-demo.js, und fügen Sie die folgenden Variablen unmittelbar vor der Zeile // Check for browser support for sessionStorage hinzu:

// App configuration
var authEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?';
var redirectUri = 'http://localhost:8080';
var appId = 'YOUR APP ID HERE';
var scopes = 'openid profile User.Read Mail.Read';

Ersetzen Sie den Wert YOUR APP ID HERE durch die Anwendungs-ID, die Sie als Teil des App-Registrierungsvorgangs generiert haben.

Suchen Sie jetzt die Zeile // OAUTH FUNCTIONS in outlook-demo.js. Fügen Sie nach dieser Zeile die folgende Funktion hinzu.

Die Funktion buildAuthUrl

function buildAuthUrl() {
  // Generate random values for state and nonce
  sessionStorage.authState = guid();
  sessionStorage.authNonce = guid();

  var authParams = {
    response_type: 'id_token token',
    client_id: appId,
    redirect_uri: redirectUri,
    scope: scopes,
    state: sessionStorage.authState,
    nonce: sessionStorage.authNonce,
    response_mode: 'fragment'
  };

  return authEndpoint + $.param(authParams);
}

Suchen Sie die Zeile // HELPER FUNCTIONS, und fügen Sie danach die folgende Funktion hinzu.

Die Funktion guid

function guid() {
  var buf = new Uint16Array(8);
  cryptObj.getRandomValues(buf);
  function s4(num) {
    var ret = num.toString(16);
    while (ret.length < 4) {
      ret = '0' + ret;
    }
    return ret;
  }
  return s4(buf[0]) + s4(buf[1]) + '-' + s4(buf[2]) + '-' + s4(buf[3]) + '-' +
    s4(buf[4]) + '-' + s4(buf[5]) + s4(buf[6]) + s4(buf[7]);
}

Aktualisieren Sie jetzt die Funktion renderWelcome, um die Funktion buildAuthUrl aufzurufen, und legen Sie das href-Attribut der Anmeldeschaltfläche auf das Ergebnis fest. Ersetzen Sie die folgende Zeile:

$('#connect-button').attr('href', '#');

Durch:

$('#connect-button').attr('href', buildAuthUrl());

Speichern Sie Ihre Änderungen, und navigieren Sie zu http://localhost:8080. Wenn Sie den Mauszeiger über die Schaltfläche Mit Outlook verbinden bewegen, sollte diese folgendermaßen aussehen:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=id_token+token&client_id=<SOME GUID>&redirect_uri=http%3A%2F%2Flocalhost%3A8080&scope=openid+profile+User.Read+Mail.Read&state=f175f48d-d277-9893-9c8d-dcc2a95ffe16&nonce=593a2b06-d77b-31c2-ae43-e74c0ebeb304&response_mode=fragment

Der Abschnitt <SOME GUID> sollte mit Ihrer Client-ID übereinstimmen. Klicken Sie auf den Link. Daraufhin wird die Anmeldeseite angezeigt. Melden Sie sich mit Ihrem Office 365- oder Outlook.com-Konto an. Ihr Browser sollte Sie ohne sichtbare Änderungen zurück zu unserer Startseite leiten.

Der Grund, warum es keine Änderung gibt, liegt darin, dass wir keinen Code geschrieben haben, um die Umleitung zurück vom Azure-Authentifizierungsendpunkt zu verarbeiten. Das werden wir jetzt tun.

Abrufen des Tokens

Suchen Sie die render -Funktion in outlook-demo.js. Fügen Sie unmittelbar nach der Zeile // Receive access token den folgenden Code hinzu.

Neuen Code zum Abrufen von Zugriffstoken

'#access_token': function() {
  handleTokenResponse(hash);             
},

Fügen Sie als Nächstes den folgenden Code nach der Funktion buildAuthUrl hinzu.

Die Funktion handleTokenResponse

function handleTokenResponse(hash) {
  // clear tokens
  sessionStorage.removeItem('accessToken');
  sessionStorage.removeItem('idToken');

  var tokenresponse = parseHashParams(hash);

  // Check that state is what we sent in sign in request
  if (tokenresponse.state != sessionStorage.authState) {
    sessionStorage.removeItem('authState');
    sessionStorage.removeItem('authNonce');
    // Report error
    window.location.hash = '#error=Invalid+state&error_description=The+state+in+the+authorization+response+did+not+match+the+expected+value.+Please+try+signing+in+again.';
    return;
  }

  sessionStorage.authState = '';
  sessionStorage.accessToken = tokenresponse.access_token;

  // Get the number of seconds the token is valid for,
  // Subract 5 minutes (300 sec) to account for differences in clock settings
  // Convert to milliseconds
  var expiresin = (parseInt(tokenresponse.expires_in) - 300) * 1000;
  var now = new Date();
  var expireDate = new Date(now.getTime() + expiresin);
  sessionStorage.tokenExpires = expireDate.getTime();

  sessionStorage.idToken = tokenresponse.id_token;

  // Redirect to home page
  window.location.hash = '#';   
}

Diese Funktion löscht alle zwischengespeicherten Token und analysiert dann die Tokenantwort. Sie überprüft, ob der Parameter state dem entspricht, was ursprünglich gesendet wurde, berechnet den Ablaufzeitpunkt für das Token und speichert dann die Token in der Sitzung. Bevor wir diesen Code testen, müssen wir Implementieren die Funktion parseHashParams implementieren. Fügen Sie diese Funktion nach der Funktion guid hinzu.

Die Funktion parseHashParams

function parseHashParams(hash) {
  var params = hash.slice(1).split('&');

  var paramarray = {};
  params.forEach(function(param) {
    param = param.split('=');
    paramarray[param[0]] = param[1];
  });

  return paramarray;
}

Nun werden wir die Funktion render so aktualisieren, dass geprüft wird, ob der Benutzer authentifiziert wurde. Ersetzen Sie die Zeile var isAuthenticated = false; durch den folgenden Code.

Der Code ist in der render-Funktion vorhanden.

// Check for presence of access token
var isAuthenticated = (sessionStorage.accessToken != null && sessionStorage.accessToken.length > 0);
renderNav(isAuthenticated);
renderTokens();

Wir werden nun eine Funktion zum Ändern der Navigationsleiste in Abhängigkeit davon hinzufügen, ob ein Benutzer authentifiziert wurde oder nicht. Fügen Sie die folgende Funktion nach der Funktion setActiveNav hinzu.

Die Funktion renderNav

function renderNav(isAuthed) {
  if (isAuthed) {
    $('.authed-nav').show();
  } else {
    $('.authed-nav').hide();
  }
}

Wir werden nun auch eine Funktion zum Anzeigen der Token hinzufügen, damit wir bestätigen können, das alles funktioniert. Fügen Sie die folgende Funktion nach der Funktion renderNav hinzu.

Die Funktion renderTokens

function renderTokens() {
  if (sessionStorage.accessToken) {
    // For demo purposes display the token and expiration
    var expireDate = new Date(parseInt(sessionStorage.tokenExpires));
    $('#token', window.parent.document).text(sessionStorage.accessToken);
    $('#expires-display', window.parent.document).text(expireDate.toLocaleDateString() + ' ' + expireDate.toLocaleTimeString());
    if (sessionStorage.idToken) {
      $('#id-token', window.parent.document).text(sessionStorage.idToken);
    }
    $('#token-display', window.parent.document).show();
  } else {
    $('#token-display', window.parent.document).hide();
  }
}

Wir müssen jetzt Code zu index.html hinzufügen, um unsere neue Funktion renderTokens zu unterstützen. Fügen Sie den folgenden Code nach der Zeile <!-- token display --> in index.html hinzu:

Neuer Code in index.html

<div id="token-display" class="page panel panel-default">
  <div class="panel-body">
    <h4>Access Token:</h4>
    <pre><code id="token"></code></pre>
    <h4>Expires:</h4>
    <p id="expires-display"></p>
    <h4>ID Token:</h4>
    <pre><code id="id-token"></code></pre>
  </div>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Je nach den Status der Sitzung müssen Sie sich möglicherweise erneut anmelden. Nach der Anmeldung wird Folgendes angezeigt.

Die App, die Token anzeigt

Wenn diese nicht angezeigt wird, wird vom Authentifizierungsprozess möglicherweise ein Fehler zurückgegeben. Wir fügen nun Code zu Behandlung einer Fehlerantwort hinzu. Fügen Sie als Erstes den folgenden Code nach der Zeile // Error display in der Funktion render hinzu:

#error-Handler in der render-Funktion

'#error': function () {
  var errorresponse = parseHashParams(hash);
  renderError(errorresponse.error, errorresponse.error_description);
},

Fügen Sie diese Funktion dann nach der Funktion renderToken hinzu.

Die Funktion renderError

function renderError(error, description) {
  $('#error-name', window.parent.document).text('An error occurred: ' + decodePlusEscaped(error));
  $('#error-desc', window.parent.document).text(decodePlusEscaped(description));
  $('#error-display', window.parent.document).show();
}

Fügen Sie decodePlusEscaped unmittelbar nach der Funktion parseHashParams hinzu.

Die Funktion decodePlusEscaped

function decodePlusEscaped(value) {
  // decodeURIComponent doesn't handle spaces escaped
  // as '+'
  if (value) {
    return decodeURIComponent(value.replace(/\+/g, ' '));
  } else {
    return '';
  }
}

Zum Schluss fügen Sie den HTML-Code zu index.html hinzu, um den Fehler anzuzeigen. Fügen Sie den folgenden Code nach der Zeile <!-- error message --> hinzu:

Neuer Code in index.html

<div id="error-display" class="page panel panel-danger">
  <div class="panel-heading">
    <h3 class="panel-title" id="error-name"></h3>
  </div>
  <div class="panel-body">
    <pre><code id="error-desc"></code></pre>
  </div>
</div>

Wenn Sie jetzt Ihren Browser aktualisieren und der Authentifizierungsprozess einen Fehler zurückgibt, wird dieser angezeigt.

Tipp

Wenn Sie die Fehleranzeige testen möchten, können Sie einfach einen Fehler generieren, indem Sie den Anmeldevorgang mit einem neuen Benutzer starten (ein Benutzer, der sich zuvor noch nicht bei der App angemeldet hat). Wenn Sie aufgefordert werden, der App die Berechtigung zu erteilen, wählen Sie Nein oder Abbrechen aus. Auf diese Weise werden Sie zurück zur App geleitet, und es wird ein Fehler angezeigt, der besagt, dass der Benutzer keine Berechtigung erteilt hat.

Bevor wir dies verbessern, implementieren wir nun die Möglichkeit des Abmeldens. Da alles in der Sitzung gespeichert wird, können wir die Sitzung einfach leeren.

Fügen Sie als Erstes den folgenden Code nach der Zeile // Signout in der Funktion render hinzu:

#signout-Handler in der render-Funktion

'#signout': function () {
  clearUserState();

  // Redirect to home page
  window.location.hash = '#';
},

Fügen Sie als Nächstes die folgende Funktion nach der Funktion decodePlusEscaped hinzu.

Die Funktion clearUserState

function clearUserState() {
  // Clear session
  sessionStorage.clear();
}

Speichern Sie nun Ihre Änderungen, und aktualisieren Sie Ihren Browser. Sie sollten sich über die Schaltfläche Abmelden abmelden können.

Verwenden des ID-Tokens

An diesem Punkt verfügen wir über ein Zugriffstoken, wir könnten also einfach fortfahren und die Outlook-API aufrufen. Zugriffstoken sind jedoch relativ kurzlebig. Sie laufen nach einer Stunde ab. Wir könnten den Benutzer zwar bitten, sich erneut anzumelden, dies ist aber keine gute Lösung. Im Idealfall sollten wir in der Lage sein, automatisch ein neues Token anzufordern. Die implizite Genehmigungsfluss verwendet nicht die Funktion zum Aktualisieren von Token, um ein neues Token zu erhalten, muss also eine neue Anforderung an den Autorisierungsendpunkt gesendet werden. Um dies automatisch auszuführen, müssen wir den Anmeldenamen des Benutzers einschließen, den wir aus dem ID-Token abrufen können, das in der anfänglichen Autorisierungsantwort enthalten ist. Wir können auch Daten wie den Anzeigenamen des Benutzers abrufen, den wir zum Personalisieren der App verwenden können.

Entsprechend der Azure-Dokumentation und der OpenID-Spezifikation MÜSSEN wir das Token vor der Verwendung überprüfen. Unsere Funktion führt daher eine grundlegende Validierung beim Analysieren des Tokens aus.

Wichtig

In diesem Beispiel werden nicht alle erforderlichen Prüfungen ausgeführt, die in der OpenID-Spezifikation aufgeführt sind. Vor allem wird die Signatur im Token nicht überprüft. Für das Anfordern der Signierschlüssel aus Azure wäre derzeit eine serverseitige Komponente erforderlich, wir überspringen daher der Einfachheit halber diesen Schritt. In Produktions-Apps sollte dieser wichtige Schritt aber nicht übersprungen werden.

Fügen Sie die folgende Funktion in outlook-demo.js nach der Funktion handleTokenResponse hinzu.

Die Funktion validateIdToken

function validateIdToken(callback) {
  // Per Azure docs (and OpenID spec), we MUST validate
  // the ID token before using it. However, full validation
  // of the signature currently requires a server-side component
  // to fetch the public signing keys from Azure. This sample will
  // skip that part (technically violating the OpenID spec) and do
  // minimal validation

  if (null == sessionStorage.idToken || sessionStorage.idToken.length <= 0) {
    callback(false);
  }

  // JWT is in three parts seperated by '.'
  var tokenParts = sessionStorage.idToken.split('.');
  if (tokenParts.length != 3){
    callback(false);
  }

  // Parse the token parts
  var header = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(tokenParts[0]));
  var payload = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(tokenParts[1]));

  // Check the nonce
  if (payload.nonce != sessionStorage.authNonce) {
    sessionStorage.authNonce = '';
    callback(false);
  }

  sessionStorage.authNonce = '';

  // Check the audience
  if (payload.aud != appId) {
    callback(false);
  }

  // Check the issuer
  // Should be https://login.microsoftonline.com/{tenantid}/v2.0
  if (payload.iss !== 'https://login.microsoftonline.com/' + payload.tid + '/v2.0') {
    callback(false);
  }

  // Check the valid dates
  var now = new Date();
  // To allow for slight inconsistencies in system clocks, adjust by 5 minutes
  var notBefore = new Date((payload.nbf - 300) * 1000);
  var expires = new Date((payload.exp + 300) * 1000);
  if (now < notBefore || now > expires) {
    callback(false);
  }

  // Now that we've passed our checks, save the bits of data
  // we need from the token.

  sessionStorage.userDisplayName = payload.name;
  sessionStorage.userSigninName = payload.preferred_username;

  // Per the docs at:
  // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/#send-the-sign-in-request
  // Check if this is a consumer account so we can set domain_hint properly
  sessionStorage.userDomainType = 
    payload.tid === '9188040d-6c67-4c5b-b112-36a304b66dad' ? 'consumers' : 'organizations';

  callback(true);
}

Zusammenfassend verwendet die Funktion die jsrsasign-Bibliothek, um das Token zu analysieren, und überprüft dann Nonce, Zielgruppe, Aussteller und gültige Datumsangaben. Wenn alle Elemente die Überprüfung bestehen, werden drei Datenelemente aus dem Token extrahiert:

  • Der Anzeigename des Benutzers, den wir verwenden werden, um den Benutzer nach der Anmeldung zu begrüßen.
  • Der bevorzugte Benutzername des Benutzers, den wir für automatische Tokenanforderungen verwenden werden.
  • Wenn der Benutzer ein Konto eines Privatanwenders (Outlook.com) oder ein Geschäftskonto (Office 365) ist, das auch für automatische Tokenanforderungen verwendet wird.

Jetzt werden wir die Funktion handleTokenResponse so ändern, dass diese Funktion aufgerufen wird. Suchen Sie die letzten beiden Zeilen der handleTokenResponse:

// Redirect to home page
window.location.hash = '#';

Ersetzen Sie diese Zeilen durch Folgendes:

validateIdToken(function(isValid) {
  if (isValid) {
    // Re-render token to handle refresh
    renderTokens();

    // Redirect to home page
    window.location.hash = '#';
  } else {
    clearUserState();
    // Report error
    window.location.hash = '#error=Invalid+ID+token&error_description=ID+token+failed+validation,+please+try+signing+in+again.';
  }
});

Nun fügen Sie den HTML-Code zu index.html hinzu, um eine Willkommensseite anzuzeigen. Fügen Sie den folgenden Code nach der Zeile <!-- logged in user welcome --> hinzu:

Neuer Code in index.html

<div id="logged-in-welcome" class="jumbotron page">
  <h1>Outlook SPA Demo</h1>
  <p>Welcome <span id="username"></span>! Please use the nav menu to access your Outlook data.</p>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Nach der Anmeldung sollte nun eine Willkommensnachricht mit dem Anzeigenamen des Benutzers angezeigt werden.

Aktualisieren des Zugriffstokens

Befassen wir uns nun mit einer weiteren OAuth-Aufgabe, bevor wir mit Outlook-Daten weitermachen: Das Aktualisieren von Token. Gemäß der Azure-Dokumentation wird hierfür eine ausgeblendete iframe-Anforderung verwendet. Im Wesentlichen wird ein iframe in die Seite eingefügt, und der Azure-Authentifizierungsendpunkt wird damit geladen. Wir fügen ein paar zusätzliche Parameter zu der URL hinzu, um die Anforderung automatisch auszuführen.

Fügen Sie die folgende Funktion zu outlook-demo.js nach der Funktion validateIdToken hinzu.

Die Funktion makeSilentTokenRequest

function makeSilentTokenRequest(callback) {
  // Build up a hidden iframe
  var iframe = $('<iframe/>');
  iframe.attr('id', 'auth-iframe');
  iframe.attr('name', 'auth-iframe');
  iframe.appendTo('body');
  iframe.hide();

  iframe.load(function() {
    callback(sessionStorage.accessToken);
  });

  iframe.attr('src', buildAuthUrl() + '&prompt=none&domain_hint=' + 
    sessionStorage.userDomainType + '&login_hint=' + 
    sessionStorage.userSigninName);
}

Beachten Sie, dass wir dieselbe buildAuthUrl-Funktion verwenden, um die Autorisierungs-URL abzurufen, wir fügen aber zusätzliche Abfrageparameter hinzu:

  • prompt=none: Wir fordern an, dass keine Anmeldeaufforderung angezeigt wird. Dies bewirkt, dass in Azure entweder die Token ohne Aufforderung zurückgegeben werden oder dass ein Fehler zurückgegeben wird, in dem erläutert wird, warum keine Token zurückgegeben werden können.
  • domain_hint=consumers oder domain_hint=organizations: Azure benötigt diese Informationen, um die automatische Anforderung zu verarbeiten. Diese teilen Azure im Wesentlichen mit, ob es sich bei dem Konto um ein Outlook.com-Konto oder um ein Office 365-Konto handelt.
  • login_hint=<username>: Der Anmeldename des Benutzers. Azure benötigt diesen, um die automatische Anforderung zu verarbeiten.

Nun benötigen wir einen Ort, wo wir diese Funktion verwenden können. Fügen Sie die folgende Funktion nach der Funktion makeSilentTokenRequest hinzu.

Die Funktion getAccessToken

// Helper method to validate token and refresh
// if needed
function getAccessToken(callback) {
  var now = new Date().getTime();
  var isExpired = now > parseInt(sessionStorage.tokenExpires);
  // Do we have a token already?
  if (sessionStorage.accessToken && !isExpired) {
    // Just return what we have
    if (callback) {
      callback(sessionStorage.accessToken);
    }
  } else {
    // Attempt to do a hidden iframe request
    makeSilentTokenRequest(callback);
  }
}

Wir verwenden diese Methode immer dann, wenn wir die Zugriffstoken verwenden müssen. Die Funktion überprüft, ob das Token bald abläuft. Falls nicht, wird das Token nur aus der Sitzung zurückgegeben. Falls schon, wird das Token aktualisiert.

Wenn eine automatische Tokenanforderung vorgenommen wird und ein Problem mit dem Cookie des Benutzers vorliegt, kann der Azure-Dienst einen login_required- oder interaction_required-Fehler zurückgeben, in dem darauf hingewiesen wird, dass sich der Benutzer erneut anmelden muss. Wir fügen nun Code zu unserem #error-Handler hinzu, um diese Fehler abzufangen und den Browser zu einer interaktiven Anmeldung umzuleiten. Ersetzen Sie den vorhandenen #error-Handler in der Funktion render durch Folgendes:

Neuer #error-Handler in der Funktion render

'#error': function () {
  var errorresponse = parseHashParams(hash);
  if (errorresponse.error === 'login_required' ||
      errorresponse.error === 'interaction_required') {
    // For these errors redirect the browser to the login
    // page.
    window.location = buildAuthUrl();
  } else {
    renderError(errorresponse.error, errorresponse.error_description);
  }
},

Zum Schluss fügen wir Code hinzu, um den ausgeblendeten iframe zu entfernen, sobald dessen Funktion abgeschlossen ist. Fügen Sie die folgenden Zeilen im oberen Bereich der Funktion handleTokenResponse hinzu:

// If this was a silent request remove the iframe
$('#auth-iframe').remove();

Verwenden der Mail-API

Da wir nun ein Zugriffstoken abrufen können, bietet es sich an, mit der Mail-API fortzufahren. Beginnen wir damit, die JavaScript-Clientbibliothek von Microsoft Graph herunterzuladen. Wir haben bereits einen Verweis darauf in der HTML hinzugefügt, wir müssen sie also nur herunterladen. Wechseln Sie zu https://github.com/microsoftgraph/msgraph-sdk-javascript, und laden Sie die Quelle herunter. Kopieren Sie ./lib/graph-js-sdk-web.js in das Verzeichnis javascript-tutorial.

Unsere erste Aufgabe mit dieser API besteht darin, die E-Mail-Adresse des Benutzers abzurufen. Wir verwenden diese, um den X-AnchorMailbox-Header festzulegen. Durch Festlegen dieses Headers auf die E-Mail-Adresse des Benutzers können die Outlook-Server die Anforderung schneller weiterleiten, es wird daher empfohlen, diesen festzulegen. Wir implementieren nun eine Methode, um die E-Mail-Adresse des Benutzers aus der Outlook-API abzurufen.

Hinweis

Sie fragen sich vielleicht, warum wir hier nicht einfach den preferred_username-Wert aus dem Benutzer-ID-Token verwenden und somit keine zusätzliche API-Anforderung stellen müssen. In vielen Fällen ist dieser Wert möglicherweise identisch mit der E-Mail-Adresse des Benutzers, dies gilt jedoch nicht für alle Konten. Ein Benutzer könnte beispielsweise so konfiguriert sein, dass er sich mit einem Skype-Namen oder einer Mobiltelefonnummer anmelden kann. Durch Anfordern der E-Mail-Adresse aus der API können wir sicher sein, dass wir immer die korrekte E-Mail-Adresse erhalten.

Fügen Sie die folgende Funktion nach der Zeile // OUTLOOK API FUNCTIONS hinzu.

Die Funktion getUserEmailAddress

function getUserEmailAddress(callback) {
  if (sessionStorage.userEmail) {
    callback(sessionStorage.userEmail);
  } else {
    getAccessToken(function(accessToken) {
      if (accessToken) {
        // Create a Graph client
        var client = MicrosoftGraph.Client.init({
          authProvider: (done) => {
            // Just return the token
            done(null, accessToken);
          }
        });

        // Get the Graph /Me endpoint to get user email address
        client
          .api('/me')
          .get((err, res) => {
            if (err) {
              callback(null, err);
            } else {
              callback(res.mail);
            }
          });
      } else {
        var error = { responseText: 'Could not retrieve access token' };
        callback(null, error);
      }
    });
  }
}

Diese Funktion überprüft, ob wir einen Wert in der Sitzung gespeichert haben; falls nicht, wird die API aufgerufen, um die E-Mail-Adresse des Benutzers zu erhalten. Da dies das erste Mal ist, dass wir die Graph-Clientbibliothek verwenden, sollten wir sie testen, bevor wir fortfahren.

Fügen den folgenden Code nach der Zeile // Display inbox in der Funktion render hinzu.

#inbox-Handler in der Funktion render

'#inbox': function () {
  if (isAuthenticated) {
    renderInbox();  
  } else {
    // Redirect to home page
    window.location.hash = '#';
  }
},

Fügen Sie nun die folgende Funktion nach der Funktion renderWelcome hinzu.

Die Funktion renderInbox

function renderInbox() {
  setActiveNav('#inbox-nav');
  $('#inbox-status').text('Loading...');
  $('#message-list').empty();
  $('#inbox').show();
  // Get user's email address
  getUserEmailAddress(function(userEmail, error) {
    if (error) {
      renderError('getUserEmailAddress failed', error.responseText);
    } else {
      $('#inbox-status').text('Your email address is: ' + userEmail);
    }
  });
}

Fügen Sie den folgenden Code zu index.html nach der Zeile <!-- inbox display --> hinzu:

Neuer Code in index.html

<div id="inbox" class="page panel panel-default">
  <div class="panel-heading">
    <h1 class="panel-title">Inbox</h1>
  </div>
  <div id="inbox-status" class="panel-body">
  </div>
  <div class="list-group" id="message-list">
  </div>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Nach der Anmeldung klicken Sie auf die Schaltfläche Posteingang in der Navigationsleiste. Wenn alles ordnungsgemäß funktioniert, sollte die E-Mail-Adresse des Benutzers auf der Seite angezeigt werden. Nun wollen wir die Nachrichten des Benutzers abrufen.

Fügen Sie die folgende Funktion zu outlook-demo.js nach der Funktion getUserEmailAddress hinzu.

Die Funktion getUserInboxMessages

function getUserInboxMessages(emailAddress, callback) {
  getAccessToken(function(accessToken) {
    if (accessToken) {
      // Create a Graph client
      var client = MicrosoftGraph.Client.init({
        authProvider: (done) => {
          // Just return the token
          done(null, accessToken);
        }
      });

      // Get the 10 newest messages
      client
        .api('/me/mailfolders/inbox/messages')
        .header('X-AnchorMailbox', emailAddress)
        .top(10)
        .select('subject,from,receivedDateTime,bodyPreview')
        .orderby('receivedDateTime DESC')
        .get((err, res) => {
          if (err) {
            callback(null, err);
          } else {
            callback(res.value);
          }
        });
    } else {
      var error = { responseText: 'Could not retrieve access token' };
      callback(null, error);
    }
  });
}

Diese verwendet den Graph-Client, den wir in getUserEmailAddress verwendet haben, dieses Mal ist der Aufruf aber etwas komplizierter. Die Funktion verwendet Abfrageparameter, um die zurückgegebenen Ergebnisse zu steuern.

  • Mithilfe der top-Methode werden die Ergebnisse auf die ersten 10 Nachrichten beschränkt.
  • Mithilfe der select-Methode wird gesteuert, welche Felder für jede Nachricht zurückgegeben werden. In diesem Fall fordern wir nur die Felder subject, from, receivedDateTime und bodyPreview an.
  • Mithilfe der orderby-Methode werden die Ergebnisse nach dem Feld receivedDateTime in absteigender Reihenfolge (neueste zuerst) sortiert.

Jetzt werden wir die Funktion renderInbox so ändern, dass diese Funktion aufgerufen wird. Ersetzen Sie die Zeile $('#inbox-status').text('Your email address is: ' + userEmail); durch den folgenden Code.

getUserInboxMessages(userEmail, function(messages, error){
  $('#inbox-status').text(JSON.stringify(messages));
});

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Beim Klicken auf die Schaltfläche Posteingang wird schließlich eine unformatierte Sicherungskopie von JSON-Nachrichten angezeigt. Diese ist nicht schön, bestätigt aber, dass unser API-Aufruf funktioniert. Wir korrigieren die App nun so, dass die Ergebnisse etwas ansprechender angezeigt werden.

Anzeigen der Ergebnisse

Wir fügen nun eine Handlebar-Vorlage zu unserer Seite hinzu, um die Nachrichten anzuzeigen. Fügen Sie den folgenden Code zwischen dem letzten </div>- und </body>-Tags hinzu:

<!-- Handlebars template for message list -->
<script id="msg-list-template" type="text/x-handlebars-template">
  {{#each messages}}
  <div class="list-group-item">
    <h3 id="msg-from" class="list-group-item-">{{this.from.emailAddress.name}}</h3>
    <h4 id="msg-subject" class="list-group-item-heading">{{this.subject}}</h4>
    <p id="msg-received" class="list-group-item-heading text-muted"><em>Received: {{formatDate this.receivedDateTime}}</em></p>
    <p id="msg-preview" class="list-group-item-text text-muted"><em>{{this.bodyPreview}}</em></p>
  </div>
  {{/each}}
</script>

Um Datumsangaben ansprechend zu gestalten, registrieren wir ein Handlebar-Hilfsprogramm zum Formatieren von Datumsangaben. Fügen Sie die folgende Funktion nach der Funktion clearUserState in outlook-demo.js hinzu.

Das formatDate-Hilfsprogramm

Handlebars.registerHelper("formatDate", function(datetime){
  // Dates from API look like:
  // 2016-06-27T14:06:13Z

  var date = new Date(datetime);
  return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
});

Ersetzen Sie schließlich die $('#inbox-status').text(JSON.stringify(messages));-Funktion in der renderInbox-Funktion durch den folgenden Code.

if (error) {
  renderError('getUserInboxMessages failed', error);
} else {
  $('#inbox-status').text('Here are the 10 most recent messages in your inbox.');
  var templateSource = $('#msg-list-template').html();
  var template = Handlebars.compile(templateSource);

  var msgList = template({messages: messages});
  $('#message-list').append(msgList);
}

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser.

Eine formatierte Liste von Nachrichten.

Hinzufügen von Kalender- und Kontakt-APIs

Da Sie nun das Aufrufen der Outlook-Mail-API gemeistert haben, dürfte es kein Problem mehr sein, das gleiche für Kalender- und Kontakte-APIs zu tun.

Tipp

Wenn Sie die Schritte in diesem Lernprogramm befolgt haben, haben Sie wahrscheinlich ein Zugriffstoken in Ihrem Sitzungscookie gespeichert. Dieses Token ist nur für den Mail.Read-Bereich gültig. Um die Kalender- oder Kontakte-API aufzurufen, müssen wir neue Bereiche hinzufügen. Melden Sie sich unbedingt von der App ab, um das Sitzungscookie zu entfernen, damit Sie den Anmeldevorgang von vorne beginnen können, um ein neues Zugriffstoken zu erhalten.

Für die Kalender-API:

  1. Aktualisieren Sie die Variable scopes in outlook-demo.js, um den Calendars.Read-Bereich einzuschließen.

    var scopes = 'openid profile User.Read Mail.Read Calendars.Read';
    
  2. Fügen Sie einen #calendar-Handler zu der Funktion render hinzu.

    // Display calendar
    '#calendar': function () {
      if (isAuthenticated) {
        renderCalendar();  
      } else {
        // Redirect to home page
        window.location.hash = '#';
      }
    },
    
  3. Fügen Sie eine renderCalendar-Funktion zu outlook-demo.js hinzu.

    function renderCalendar() {
      setActiveNav('#calendar-nav');
      $('#calendar-status').text('Loading...');
      $('#event-list').empty();
      $('#calendar').show();
      // Get user's email address
      getUserEmailAddress(function(userEmail, error) {
        if (error) {
          renderError('getUserEmailAddress failed', error.responseText);
        } else {
          getUserEvents(userEmail, function(events, error){
            if (error) {
              renderError('getUserEvents failed', error);
            } else {
              $('#calendar-status').text('Here are the 10 most recently created events on your calendar.');
              var templateSource = $('#event-list-template').html();
              var template = Handlebars.compile(templateSource);
    
              var eventList = template({events: events});
              $('#event-list').append(eventList);
            }
          });
        }
      });
    }
    
  4. Fügen Sie eine getUserEvents-Funktion zu outlook-demo.js hinzu.

    function getUserEvents(emailAddress, callback) {
      getAccessToken(function(accessToken) {
        if (accessToken) {
          // Create a Graph client
          var client = MicrosoftGraph.Client.init({
            authProvider: (done) => {
              // Just return the token
              done(null, accessToken);
            }
          });
    
          // Get the 10 newest events
          client
            .api('/me/events')
            .header('X-AnchorMailbox', emailAddress)
            .top(10)
            .select('subject,start,end,createdDateTime')
            .orderby('createdDateTime DESC')
            .get((err, res) => {
              if (err) {
                callback(null, err);
              } else {
                callback(res.value);
              }
            });
        } else {
          var error = { responseText: 'Could not retrieve access token' };
          callback(null, error);
        }
      });
    }
    
  5. Fügen Sie eine Kalender-Schaltfläche zur Navigationsleiste in index.html hinzu.

    <li id='calendar-nav'><a href="#calendar">Calendar</a></li>
    
  6. Fügen Sie eine Kalenderanzeige zu index.html hinzu.

    <!-- calendar display -->
    <div id="calendar" class="page panel panel-default">
      <div class="panel-heading">
        <h1 class="panel-title">Calendar</h1>
      </div>
      <div id="calendar-status" class="panel-body">
      </div>
      <div class="list-group" id="event-list">
      </div>
    </div>
    
  7. Fügen Sie eine Handlebars-Vorlage für die Ereignisliste zu index.html hinzu.

    <!-- Handlebars template for event list -->
    <script id="event-list-template" type="text/x-handlebars-template">
      {{#each events}}
      <div class="list-group-item">
        <h4 id="event-subject" class="list-group-item-heading">{{this.subject}}</h4>
        <p id="event-start" class="list-group-item-heading">Start: {{formatDate this.start.dateTime}}</p>
        <p id="event-end" class="list-group-item-heading">End: {{formatDate this.end.dateTime}}</p>
      </div>
      {{/each}}
    </script>
    
  8. Aktualisieren Sie den Browser. Nach der Anmeldung klicken Sie auf die Schaltfläche Kalender in der Navigationsleiste.

Für die Kontakte-API:

  1. Aktualisieren Sie die Variable scopes in outlook-demo.js, um den Contacts.Read-Bereich einzuschließen.

    var scopes = 'openid profile User.Read Mail.Read Contacts.Read';
    
  2. Fügen Sie einen #contacts-Handler zu der Funktion render hinzu.

    // Display contacts
    '#contacts': function () {
      if (isAuthenticated) {
        renderContacts();  
      } else {
        // Redirect to home page
        window.location.hash = '#';
      }
    },
    
  3. Fügen Sie eine renderContacts-Funktion zu outlook-demo.js hinzu.

    function renderContacts() {
      setActiveNav('#contacts-nav');
      $('#contacts-status').text('Loading...');
      $('#contact-list').empty();
      $('#contacts').show();
      // Get user's email address
      getUserEmailAddress(function(userEmail, error) {
        if (error) {
          renderError('getUserEmailAddress failed', error.responseText);
        } else {
          getUserContacts(userEmail, function(contacts, error){
            if (error) {
              renderError('getUserContacts failed', error);
            } else {
              $('#contacts-status').text('Here are your first 10 contacts.');
              var templateSource = $('#contact-list-template').html();
              var template = Handlebars.compile(templateSource);
    
              var contactList = template({contacts: contacts});
              $('#contact-list').append(contactList);
            }
          });
        }
      });
    }
    
  4. Fügen Sie eine getUserContacts-Funktion zu outlook-demo.js hinzu.

    function getUserContacts(emailAddress, callback) {
      getAccessToken(function(accessToken) {
        if (accessToken) {
          // Create a Graph client
          var client = MicrosoftGraph.Client.init({
            authProvider: (done) => {
              // Just return the token
              done(null, accessToken);
            }
          });
    
          // Get the first 10 contacts in alphabetical order
          // by given name
          client
            .api('/me/contacts')
            .header('X-AnchorMailbox', emailAddress)
            .top(10)
            .select('givenName,surname,emailAddresses')
            .orderby('givenName ASC')
            .get((err, res) => {
              if (err) {
                callback(null, err);
              } else {
                callback(res.value);
              }
            });
        } else {
          var error = { responseText: 'Could not retrieve access token' };
          callback(null, error);
        }
      });
    }
    
  5. Fügen Sie eine Kontakte-Schaltfläche zur Navigationsleiste in index.html hinzu.

    <li id='contacts-nav'><a href="#contacts">Contacts</a></li>
    
  6. Fügen Sie eine Kontaktanzeige zu index.html hinzu.

    <!-- contacts display -->
    <div id="contacts" class="page panel panel-default">
      <div class="panel-heading">
        <h1 class="panel-title">Contacts</h1>
      </div>
      <div id="contacts-status" class="panel-body">
      </div>
      <div class="list-group" id="contact-list">
      </div>
    </div>
    
  7. Fügen Sie eine Handlebars-Vorlage für die Kontakteliste zu index.html hinzu.

    <!-- Handlebars template for contact list -->
    <script id="contact-list-template" type="text/x-handlebars-template">
      {{#each contacts}}
      <div class="list-group-item">
        <h4 id="contact-name" class="list-group-item-heading">{{this.givenName}} {{this.surname}}</h4>
        <p id="contact-email" class="list-group-item-heading">Email: {{this.emailAddresses.0.address}}</p>
      </div>
      {{/each}}
    </script>
    
  8. Aktualisieren Sie den Browser. Nach der Anmeldung klicken Sie auf die Schaltfläche Kontakte in der Navigationsleiste.