Mit diesem Tutorial lernt man schnell und einfach auf die Daten von Microsoft über die Graph Api (hier MSGraph genannt) zugzugreifen. Sie brauchen eine Azure Subscription auf Unternehmensebene und ein Azure AD Connector um genau dieses Lab nachvollziehen zu können.
Dabei werde folgende Punkte beschrieben
- Authentifizierung per Azure AD
- Einrichten der ASP.NET Web App
- Delegieren der Credentials an MSGraph
- MSGraph Api Funktionsumfung
- Implementieren in C# Code
Benutzerverwaltung in einer ASP.NET Webanwendung
Microsoft hat mehrfach das Authentifzierungsystem umgeworfen. Für die Anmeldung von Benutzern gibt es heute weit über Username und Passwort Kombi hinaus reichende Möglichkeiten. Dieser Blog setzt ein mit Azure Verknüpftes Áctive Directory voraus, AzureAD genannt. Dies in der gratis Variante.
Am einfachsten rufen sie direkt den Link auf um eine App zu registrieren. https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
Damit wird die neue Web App (die es noch nicht gibt) gegenüber AzureAD autorisiert. Da mit diesem Wizard auch gleich eine fertige ASP.NET Visual Studio generiert wird, trotz falscher Reihenfolge, gerade passend.
Neue App Registrierung
An dieser Stelle haben Sie die Chance auch andere Authentifizierungsverfahren zu wählen. Für diesen Walkthrough die erste Option.
In Zertifikate und Geheimnisse, erzeugen sie ein neues Secret und kopieren dieses in die Zwischenablage
Öffnen Sie Notepad (editor) und fügen das Geheimnis ein, Speichern sie diese txt Datei, aber lassen sie Notepad geöffnet. Wir benötigen dies um diverse Daten zwischen zu speichern.
Wechseln Sie in den Punkt Schnellstart. Je nach Anwendungsart hält Microsoft völlig unterschiedliche Authentifizierungsverfahren bereit. Dieser Assistent bietet für uns passend Webanwendung und im speziellen eine ASP.NET Core Web App.
Ein OAuth2 verfahren, basiert auf der Umleitung von Ihrer Anwendung zu Microsoft um dort die Anmeldung zu realisieren. Wenn Ihre App dort registriert ist, muss Microsoft wieder zu ihrer App zurück umleiten. Diese Urls werden gesetzt über den ersten blauen Button.
Wenn Ihre App mal im Internet landet und per FQDN erreichbar ist, können und müssen sie diese URLs noch im Azure Portal hinzufügen.
Aber zunächst laden sie mit dem zweiten Blauen Button “laden sie das codebeispiel herunter”. Ich empfehle dringend die Azure Seite im Browser geöffnet zu lassen, Wir brauchen sie noch.
Aber zunächst wird das Zip entpackt und das Projekt mit Visual Studio geöffnet.
Microsoft Graph SDK einbinden
Man kann mit der MSGraph API direkt her HTTP Request oder über ein .net SDK sprechen. Nun bin ich nicht so der Fan von Zeichenketten im Code. Also holen wir uns mal alle Pakete
Führen sie folgendes in der Paket Manager Console ihre offenen Visual Studio Projektes aus
1: dotnet add package Microsoft.Graph
2: dotnet add package Microsoft.Extensions.Configuration
3: dotnet add package Microsoft.Extensions.Configuration.FileExtensions
4: dotnet add package Microsoft.Extensions.Configuration.Json
5: dotnet add package Microsoft.Identity.Web.MicrosoftGraph
Eher so dem Zeitgeist folgend stelle ich meine Projekte immer auf die neueste .NET 5 Version um
Trotzdem erhalte ich eine Fehlermeldung
Schweregrad Code Beschreibung Projekt Datei Zeile Unterdrückungszustand
Fehler NU1605 Ein Downgrade des Pakets "Microsoft.Identity.Web" von 1.6.0 auf 1.4.0 wurde festgestellt. Verweisen Sie direkt aus dem Projekt auf das Paket, um eine andere Version auszuwählen.
WebApp-OpenIDConnect-DotNet -> Microsoft.Identity.Web.MicrosoftGraph 1.6.0 -> Microsoft.Identity.Web (>= 1.6.0)
WebApp-OpenIDConnect-DotNet -> Microsoft.Identity.Web (>= 1.4.0) WebApp-OpenIDConnect-DotNet C:\active-directory-aspnetcore-webapp-openidconnect-v2-aspnetcore3-1\WebApp-OpenIDConnect-DotNet.csproj 1
Also lasse ich alle Pakete per Nuget aktualisieren. Das wird bei Ihnen mit Sicherheit anders aussehen, da alles im Preview und recht volatil.
Kompiliert oder?
Dann hoffen wir, das Notepad noch offen ist. Wir brauchen Infos um die appsettings.json Datei zu füllen.
1: {
2: "AzureAd": {
3: "Instance": "https://login.microsoftonline.com/",
4: "Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
5: "ClientId": "47cc5783-4b53-4068-aaef-471c1a7a7d83",
6: "TenantId": "d045494e-fc77-4ae0-8c6b-8b4520666035",
7: "CallbackPath": "/signin-oidc",
8: "ClientSecret": "ke4vA2--4QvMKwRSfVn4U7.~lK-EU1Yt1J"
9: },
10: "DownStreamApi": {
11: "BaseUrl": "https://graph.microsoft.com/v1.0",
12: "Scopes": "user.read "
13: },
Die Werte in Zeile 5,6 und 8 müssen andere Werte beinhalten. Also nicht einfach copy Paste. Das sind die Daten vom Azure Portal, meist vorausgefüllt. Bis auf das Secret.
Noch ein bischen Arbeit in der startup.cs um das Dependency Injection Framework mit Objekten zu füllen. Ich bin nach wie vor kein Fan dieses Coding Styles (Fluent Api Builder Pattern). Aber da es so vorgegeben ist… Das vorher Nachher Szenario
1: //services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
2: // .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
3:
4: IEnumerable<String> initalScopes =
Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
5:
6: services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
7:
8: .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
9:
10: .EnableTokenAcquisitionToCallDownstreamApi(initalScopes)
11:
12: .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
13: .AddInMemoryTokenCaches();
Ein wenig Erläuterung. Die Rechte auf der API müssen konfiguriert und von der App angefordert werden. Um einen Kalendereintrag zu erstellen braucht man ein Recht, hier Scope genannt. Mit Add wird wie in aspnetcore üblich ein Objekt auf die DI Liste gelegt, So auch MSGraph.
Vom AD bekommt der angemeldete Benutzer (unsichtbar) einen Token überreicht, mit dem deine App zu MSGraph geht und um Daten bietet. Mit dem MemoryTokenCache wird verhindert, diesen Token jedes Mal neu besorgen zu müssen. Recht viel Komplexität das auf diese Weise komplett vor dir als Coder versteckt wird.
Kompiliert ja?
Dann machen wir noch eines. Ich bin kein Fan von MVC sondern bevorzuge ASP.NET Razor Pages. Kann man mischen, Also Verzeichnis Pages anlegen. Eine neue Razor Seite erzeugen mit dem Namen index.
Die Layoutvorlage muss hier temp. noch manuell eingebunden werden. Kompiliert nicht mehr!
Dummerweise müssen wir aus dem Minus überall einen Unterstrich machen. In View und .cs Datei.
1: @page
2: @model WebApp_OpenIDConnect_DotNet.pages.IndexModel
3: @{ Layout = "_Layout"; }
4:
5: <h1>Hallo Razor</h1>
6: @Model.Nutzer
Die C# Code Behind Logik
1: namespace WebApp_OpenIDConnect_DotNet.pages
2: {
3: public class IndexModel : PageModel
4: {
5: public string Nutzer { get; set; }
6: public void OnGet()
7: {
8: Nutzer = "nix";
Dann bestaunen wir das ganze mal im Browser. Werfen Sie den Debugger an
Hier werden die Rechte eingefordert. Vermutlich weil ich Admin bin, kann ich das für alle Benutzer zustimmend erledigen.
Bin ich denn schon drin?
MSGraph Api erkunden
Jetzt müssen wir noch erforschen was die Microsoft Graph API so alles für uns tun kann. Vieles, aber auch einiges nicht.
Ohne den Graph Explorer wäre ich aufgeschmissen. Öffnen sie den in einem neuen Browser Fenster.
https://developer.microsoft.com/graph/graph-explorer
Achten Sie darauf, das sie mit ihrem Domain Account angemeldet sind. Anderenfalls sie natürlich keinen Zugriff auf Unternehmensdaten haben.
Es ist bereits eine Query vorbelegt. Führen Sie diese aus. Sie erhalten ihre persönlichen Daten aus dem AD
PS meine Handynummer sollten sie nicht anrufen fake.
Nun hat mein AD Entry viel viel mehr Daten, die alle unterschlagen werden. So fehlt die noch immer immens wichtige Fax Nummer. Dafür sieht Microsoft Odata als Erweiterung vor. Per Querystring kann man filter, pagen, sortieren, auswählen und einbinden. Probieren sie mal https://graph.microsoft.com/v1.0/me?$select=faxNumber
Soweit ich die Doku lese dürfte das gar nicht funktionieren. Das ist das teuflische an der MSGraph API. Sie ist nicht konsistent und vollständig ausprogrammiert. Wenn man eine Userlist abruft kommen 100 Items max, beim Planner 400 Task. Und es ändert sich laufend.
Graph SDK in Code einbinden
Diese neu erworbene Wissen kommt nun in der C# Code Praxis zur Anwendung. Fügen Sie in der Index Razor Code Behind Datei den Konstruktor hinzu. Falls noch nie gesehen ctor [tab] [tab] per Schnellaktion. Dann wird per DI eine Referenz auf einen Graph Client geholt.
Rot ist nicht gut, weil die passenden Namensräume fehlen (Microsoft.Graph).
Alles was länger braucht, wird bei .net core asynchron ausgeführt. Dazu gehört ein API Call ganz gewiss. Deswegen muss die Onget Methode umgerüstet werden.
Der Graphclient folgt der Logik des URI Endpoints. Weil ich meine eigene Info brauche .Me.
1: public async Task<ActionResult> OnGet()
2: {
3: var u =await graphClient.Me.Request().GetAsync();
4: Nutzer = $"{u.DisplayName} : {u.FaxNumber}";
5: return Page();
6: }
Die Faxnummer wird aber leer bleiben, weil wie schon oben beschrieben nicht im Standard API Aufruf Ergebnis Set enthalten. Aber immerhin wird kein Fehler geworfen, weil das User Objekt eine Faxnummer hat.
Bonustrack Fax Nummer und Displayname
Zu´rück zum Graph Explorer den ich ausdrücklich als Spielwiese empfehle. Um der Api in die benötigen Daten zu entlocken hilft
https://graph.microsoft.com/v1.0/me?$select=faxNumber,Displayname
Alles was hinter der URL als Querystring auftaucht sind Options, die man dem Request als Parameter mit übergeben kann.
1: var options = new List<Option>();
2: options.Add(new QueryOption("$select", "faxNumber,Displayname"));
3:
4: var u = await graphClient.Me.Request(options).GetAsync();
Im Teil 2 wird ein komplexeres Beispiel am Beispiel Teams, Planner und Tasks beschrieben. So lange viel Spaß damit.