Wie in den Artikeln über MS Graph schon beschrieben, kann man damit auf vielfältigste Informationen aus der Microsoft Cloud zugreifen. 

Hierzu gibt es wichtige Voraussetzungen:

  • Die Applikation, die auf die Informationen zugreift, muss registriert sein. 
  • Die Applikation muss die richtigen Zugriffsberechtigungen haben.

Registrieren der Anwendung in Azure AD

Während man im Internet noch viele Einträge findet, dass die Applikation über das App-Registrierungsportal registrieren soll, hat sich für mich der Weg direkt aus https://portal.azure.com heraus als der bequemere Weg erwiesen. Für diejenigen, die es jedoch auch weiterhin über das App-Registrierungsportal vornehmen wollen oder müssen, hier noch einmal der Link:

Navigieren Sie zum Microsoft App-Registrierungsportal unter https://apps.dev.microsoft.com/.

Sie finden das neue Tool innerhalb des Azure-Portals unter Azure Active Directory -> App-Registrierungen (Vorschau).

Ein Klick auf Neue Registrierung startet den Neueintrag für eine neu zu registrierende Anwendung:

Tragen Sie den Namen der Applikation in das Feld Name ein. 

Unter Unterstützte Kontotypen wählen Sie, ob die Anwendung auch außerhalb des aktuellen Tenants zur Verfügung soll, andere Tenants darauf zugreifen dürfen, oder auch eine Anmeldung über ein Microsoft Konto möglich sein soll. 

Nur Konten in diesem Organisationsverzeichnis (Standardverzeichnis)
 
Alle Benutzer- und Gastkonten in Ihrem Verzeichnis können Ihre Anwendung oder API verwenden.
Verwenden Sie diese Option, wenn Ihre Zielgruppe sich innerhalb Ihrer Organisation befindet.
 
Konten in einem beliebigen Organisationsverzeichnis
 
Alle Benutzer und Gäste mit einem Geschäfts-, Schul- oder Unikonto von Microsoft können Ihre Anwendung oder API verwenden. Dazu gehören auch Bildungseinrichtungen und Unternehmen, die Office 365 verwenden.
Verwenden Sie diese Option, wenn Ihre Zielgruppe Kunden aus dem Unternehmens- oder Bildungsbereich sind.
 
Konten in allen Organisationsverzeichnissen und persönliche Microsoft-Konten (z. B. Skype, Xbox, Outlook.com)
 
Alle Benutzer mit einem Geschäfts-, Schul- oder Unikonto bzw. einem persönlichen Microsoft-Konto können Ihre Anwendung oder API verwenden. Dazu gehören Bildungseinrichten und Unternehmen, die Office 365 verwenden, wie auch persönliche Konten, die zur Anmeldung bei Diensten wie Xbox und Skype verwendet werden.
Mit dieser Option beziehen Sie die umfassendste Gruppe von Microsoft-Identitäten ein.
 
Aufgrund temporärer Unterschiede in der Funktionalität werden möglicherweise Fehler angezeigt, wenn Sie nach der Registrierung der Anwendung versuchen, zwischen unterstützten Zielgruppen zu wechseln.

Läuft die Anwendung als Webanwendung, wird als Umleitungs-URI gerne die URL der Webanwendung oder einer darunter liegenden Seiten angegeben – d.h. z. B. http://localhost:5050. Wie im Text angegeben, muss diese Angabe heute noch nicht zwingend hinterlegt werden – allerdings kommt es in vielen Fällen zu Fehlermeldungen, wenn hier kein Wert eingetragen ist. 

Nach Klick auf Registrieren werden alle Optionen für die Konfiguration der Anwendung verfügbar. 

Den erste wichtige Wert stellt die Anwendungs-ID dar. Diese werden Sie später für den Zugriff benötigen.

 

Die Konfiguration der Anwendungs- Registrierung

Drei weitere Einstellungen werden in den nächsten Schritten wichtig. 

  • Das Secret / „Geheime Clientschlüssel“
  • Authentifizierung
  • API-Berechtigungen

Das Secret – Geheime Clientschlüssel

Unter Zertifikate und „Geheimnisse“ können Sie nun entweder ein Zertifikat hochladen oder ein sogenanntes Secret erstellen. Diese werden im späteren Verlauf für die Authentifizierung genutzt.

Das Bild unten zeigt die Erstellung eines Secrets („Geheime Clientschlüssel“).

Nach dem Klick auf Hinzufügen wird das Secret erstellt. Beachten Sie auf dem folgenden Fenster den Hinweis in der Kopfzeile:

„Kopieren Sie den Wert des neuen geheimen Clientschlüssels. Er kann nach dem Verlassen dieser Seite nicht mehr abgerufen werden.“

Hier empfiehlt es sich die Informationen (Applikations ID und Secret) an einer zentralen Stelle abzulegen.

Authentifizierungseinstellungen

Die nächste Seite stellt die Authentifizierungsseite dar. Hier wählen Sie, welche Dienste für die Authentifizierungsmöglichkeiten für die Anwendung zur Verfügung stehen.  

Wenn Sie die Microsoft Authentication Library (MSAL) oder die Active Directory Authentication Library (ADAL) zum Erstellen von Anwendungen für Desktop- oder Mobilgeräte verwenden, können Sie einen der unten vorgeschlagenen Umleitungs-URIs auswählen oder oben einen benutzerdefinierten Umleitungs-URI eingeben. Weitere Informationen finden Sie in der Bibliotheksdokumentation.

Wichtig für die Anwendung ist auch die zweite Entscheidung: soll die Anwendung über ein Token zugreifen können, muss diese Option explizit aktiviert werden.

Ermöglicht einer Anwendung das Anfordern eines Tokens direkt vom Autorisierungsendpunkt. Dies empfiehlt sich nur, wenn die Anwendung eine Single-Page-Architektur (SPA) aufweist, keine Back-End-Komponenten umfasst oder eine Web-API über JavaScript aufruft.

API-Berechtigungen

Unter API-Berechtigungen geben Sie nun an, welche Berechtigungen die Anwendung beim Zugriff haben soll. Hier können Sie sehr filigran die Berechtigungen auf die unterschiedlichen Bereiche (Exchange, Azure AD, Kalender, Kontakte, Chat, …) einstellen.

Klicken Sie hierzu auf Berechtigung hinzufügen und wählen Sie den Bereich aus, für den Sie die Berechtigungen erteilen möchten.

Aktivieren Sie die entsprechenden Berechtigungen. Auf der rechten Seite neben den Berechtigungen sehen sie, ob nach der Aktivierung der Berechtigung eine „Einwilligung“ durch einen Administrator notwendig ist. Sollte das notwendig sein, können Sie diese nach dem Speichern der Änderungen über den Punkt Administratorzustimmung für das Standardverzeichnis erteilen sofort zentral ausführen.

Sollte eine Zustimmung durch einen Adminstratoren (Consent) notwendig sein, können Sie diese nach dem Speichern der Änderungen über den Punkt Administratorzustimmung für das Standardverzeichnis erteilen sofort zentral ausführen.

Ein Beispiel (C#)

In diesem Beispiel soll aus Azure AD ein Benutzer herausgesucht werden. Diese Aufgabe übernimmt Graph_Find_User. Als Parameter wird der _userPrincipalName des zu suchenden Benutzers und das Token (die Erstellung des Tokens wird weiter unten beschrieben) für den Zugriff übergeben

Für die Ausführung der Suche nutze ich ein HttpRequestMessage. Diese bekommt als Parameter „Get“ sowie die im Graph-Explorer ausgetestete URL https://graph.microsoft.com/v1.0/users/<User> mit.

Test der Abfrage im Graph Explorer

Dazu wird die String-Variable graphRequest aus dem Pfad zum Graph und den Parametern /User/ und <_userPrincipalName> zusammengesetzt.

In Zeile 14 wird über einen Header json als Format „vereinbart“.

In Zeile 15 wird der übergebene Token in die Abfrage als Header übernommen.

public ADAccount Graph_Find_User(string _userPrincipalName, string _token)
       {

            string _userID = string.Empty;
string _graphString = "https://graph.microsoft.com/v1.0/"; string _status = string.Empty; using (var _client = new HttpClient()) { string graphRequest = _graphString+ "users/"+_userPrincipalName; Uri _requestUri = new Uri(graphRequest); using (var _request = new HttpRequestMessage(HttpMethod.Get, graphRequest)) // endpoint + queryParameter = graphRequest { _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); ; Task<HttpResponseMessage> _response = _client.SendAsync(_request); _response.Wait(); var _responseTextTask = _response.Result.Content.ReadAsStringAsync(); _responseTextTask.Wait(); ADAccount _myAccount = new ADAccount(); _myAccount = JsonConvert.DeserializeObject<ADAccount>(_responseTextTask.Result); return _myAccount; } } }

Als Antwort lese ich die aus dem Graph-Explorer heraus schon zu erwartende JSON-Rückmeldung (Zeile 20/21), die ich (Zeile 23 / 24) in eine passende Klasse (ADAccount) einlese. Die Struktur der Klasse entspricht der aus dem Graph Explorer erwarteten Antwort:

Die Klasse ADAccount:

public class ADAccount
 {
     public IList<string> businessPhones { get; set; }
     public string displayName { get; set; }
     public string givenName { get; set;  }
     public string jobTitle { get; set; }
     public string mail { get; set; }
     public string mobilePhone { get; set; }
     public string officeLocation { get; set; }
     public string preferredLanguage { get; set; }
     public string surname{ get; set; }
     public string userPrincipalName { get; set; }
     public string id { get; set; }
    
 }

Authentifizierung (Token)

Um die Anwendung authentifizieren zu können (ich hatte bei der Registrierung der Anwendung die Option „Token“ aktiviert), benötige ich ein Token

Dieses erstelle ich über _authContext.AcquireTokenAsync (Zeile 25).

Da meine Anwendung inzwischen einen etwas größeren Umfang angenommen hat, sind die meisten Konfigurationsparameter inzwischen in einer Azure SQL Datenbank gespeichert. Diese lese ich mir im ersten Teil in _cfgParams (Zeile 11) ein. In der Struktur liegen nach dem Einlesen Informationen wie die Benutzer-Domäne (Domain.onmicrosoft.com), die URL für Microsoft Graph, die APP ClientID (aus der Registrierung der Anwendung) sowie das App Secret (ebenfalls aus der Registrierung der Anwendung). 

Da die App ID und das Secret in der Datenbank verschlüsselt abgelegt sind, müssen beide entschlüsselt werden. Das erledigt bei mir eine zentrale Funktion _mwPE.DecryptWithByteArray (Zeile 21). Hier könnte z. B. die Version von diesem Link genutzt werden. Haben Sie die App ID und das Secret lokal in einem String, kann das natürlich entfallen.

Der eigentliche Aufruf von AcquireTokenAsync erfolgt eigentlich async (Zeile 24). Leider hat das in meiner Konstellation zu einem Problem geführt. Daher habe ich mich entschieden, den Aufruf etwas umzuformulieren und über Task<> den Ablauf zu steuern. Hat alles funktioniert, erhalten Sie hier das Token, das beim Aufruf von Graph_Find_User neben dem _userPrincipalName übergeben wird.

private async Task<string> AppAuthenticationAsync()
       {
           //  Constants

           mwHelper.PasswordEncoder _mwPE = new mwHelper.PasswordEncoder();

           try
           {
               GetFromWS _getFromWS = new GetFromWS();
               Config_Parameter _cfgParams = new Config_Parameter();
               _cfgParams = _getFromWS.Get_Cfg_Parameters();

               var _tenant = _cfgParams._userDomain;               //  "Domain.onmicrosoft.com";
               var _resource = _cfgParams._ADAppResource;          //  https://graph.microsoft.com/";
               var _clientID = _cfgParams._ADAppClientID;
               var _secret = _cfgParams._ADAppSecret;

               
               var _authority = _cfgParams._ADAppAuthority + _tenant;  // $"https://login.microsoftonline.com/{tenant}";
               var _authContext = new AuthenticationContext(_authority);
               var _credentials = new ClientCredential(_mwPE.DecryptWithByteArray(_clientID), _mwPE.DecryptWithByteArray(_secret));

               // var _authResult = await _authContext.AcquireTokenAsync(_resource, _credentials);
               Task<AuthenticationResult> _authResult = _authContext.AcquireTokenAsync(_resource, _credentials);
               _authResult.Wait();

               return _authResult.Result.AccessToken;
           }
           catch (Exception ex)
           {
               return ("Fehler bei der Erstellung des Accesstokens" + ex.Message);
           }

       }

Eigentlich war damit nicht viel Code notwendig, um einen Benutzer in Azure AD zu suchen.

Die Zeit, die ich damit verbracht hatte, die Puzzle-Steine zusammenzufügen war eher dem Lesen und Suchen geschuldet, da der Teufel wie immer im Detail liegt. 

Ich hatte in meinem Blog mal irgendwann geschrieben, dass ich kein Entwickler wäre – dazu stehe ich noch heute. Insofern soll das Beispiel nur eine Hilfestellung sein, wie ein Einstieg gelingen kann – es ist mit Sicherheit kein Code, der allen kritischen Sichten und Anforderungen genügt. 

Im übrigen  muss ich hier vielen Kollegen für die Code-Schnipsel danken. Viele Bausteine, die auch in mein Beispiel eingeflossen sind, fand ich unter https://vincentlauzon.com, stackoverflow.com (eine wirklich großartige und hilfreiche Seite), oder auch https://dasow.com.

Eine wichtige Voraussetzung für den Code ist die Einbindung unterschiedlicher NuGet-Pakete. Ich habe in meiner gesamten Lösung folgende Pakete eingebunden:

  • Microsoft.Graph
  • Microsoft.Graph.Core
  • Microsoft.Identity.Client
  • Microsoft.IdentityModel.Clients.ActiveDirectory
  • Newtonsoft.Json

Ich hoffe, das Beispiel hilft beim Einstieg…