Tutorial: Schützen einer in einem externen Mandanten registrierten ASP.NET Core-Web-API
In dieser Tutorialreihe wird veranschaulicht, wie Sie eine registrierte Web-API im externen Mandanten schützen. In diesem Tutorial erstellen Sie eine ASP.NET Core-Web-API, die delegierte Berechtigungen für Bereiche und Anwendungsberechtigungen für App-Rollen veröffentlicht.
In diesem Tutorial:
- Konfigurieren Sie Ihre Web-API, um ihre App-Registrierungsdetails zu verwenden.
- Konfigurieren Sie Ihre Web-API für die Verwendung delegierter und Anwendungsberechtigungen, die in der App-Registrierung registriert sind
- Schützen Sie Ihre Web-API-Endpunkte
Voraussetzungen
Eine API-Registrierung, die mindestens einen Bereich (delegierte Berechtigungen) und eine App-Rolle (Anwendungsberechtigung) wie ToDoList.Read verfügbar macht. Wenn Sie dies noch nicht getan haben, registrieren Sie eine API im Microsoft Entra Admin Center, indem Sie die Registrierungsschritte ausführen. Stellen Sie sicher, dass Sie über Folgendes verfügen:
- Anwendungs-ID (Client) der Web-API
- Registrierte Verzeichnis-ID (Mandant) der Web-API
- Verzeichnisunterdomäne (Mandant), in der die Web-API registriert ist Wenn Ihre primäre Domäne beispielsweise contoso.onmicrosoft.com ist, ist Ihre Verzeichnisunterdomäne (Mandant) contoso.
- ToDoList.Read und ToDoList.ReadWrite als delegierte Berechtigungen (Bereiche), die von der Web-API verfügbar gemacht werden
- ToDoList.Read.All und ToDoList.ReadWrite.All als Anwendungsberechtigungen (App-Rollen), die von der Web-API verfügbar gemacht werden
SDK von .NET 7.0 oder höher
Visual Studio Code oder ein anderer Code-Editor
Erstellen einer ASP.NET Core-Web-API
Öffnen Sie Ihr Terminal, und navigieren Sie zu dem Ordner, in dem sich Ihr Projekt befinden soll.
Führen Sie die folgenden Befehle aus:
dotnet new webapi -o ToDoListAPI cd ToDoListAPI
Wenn Sie in einem Dialogfeld angeben müssen, ob Sie dem Projekt die erforderlichen Elemente hinzufügen möchten, wählen Sie Ja aus.
Installieren von Paketen
Installieren Sie die folgenden Pakete:
Microsoft.EntityFrameworkCore.InMemory
ermöglicht die Verwendung von Entity Framework Core mit einem IMDB-Katalog. Es ist nicht für die Verwendung in der Produktion konzipiert.Microsoft.Identity.Web
vereinfacht das Hinzufügen von Authentifizierungs- und Autorisierungsunterstützung für Webanwendungen, die in die Microsoft Identity Platform integriert werden.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Konfigurieren von Anwendungsregistrierungsdetails
Öffnen Sie die Datei appsettings.json in Ihrem App-Ordner, und fügen Sie die App-Registrierungsdetails hinzu, die Sie nach der Registrierung Ihrer Web-API notiert haben.
{
"AzureAd": {
"Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
},
"Logging": {...},
"AllowedHosts": "*"
}
Ersetzen Sie die folgenden Platzhalter wie gezeigt:
- Ersetzen Sie
Enter_the_Application_Id_Here
durch Ihre Anwendungs-ID (Client-ID). - Ersetzen Sie
Enter_the_Tenant_Id_Here
durch Ihre Verzeichnis-ID (Mandanten-ID). - Ersetzen Sie
Enter_the_Tenant_Subdomain_Here
durch Ihre Unterdomäne des Verzeichnisses (des Mandanten).
Verwenden einer benutzerdefinierten URL-Domäne (Optional)
Verwenden Sie eine benutzerdefinierte Domäne, um die Authentifizierungs-URL vollständig für Ihre Marke anzupassen. Aus Sicht der Benutzer entsteht dadurch der Eindruck, dass sie während des Authentifizierungsprozesses in Ihrer Domäne bleiben, anstatt zum Domänennamen ciamlogin.com umgeleitet zu werden.
Befolgen Sie diese Schritte, um eine benutzerdefinierte Domäne zu verwenden:
Führen Sie die Schritte unter Aktivieren von benutzerdefinierten URL-Domänen für Apps in externen Mandanten aus, um eine benutzerdefinierte Domänen-URL für Ihren externen Mandanten zu aktivieren.
Öffnen Sie die Datei appsettings.json:
- Aktualisieren Sie den Wert der
Instance
-Eigenschaft in https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. Ersetzen SieEnter_the_Custom_Domain_Here
durch die benutzerdefinierte URL-Domäne undEnter_the_Tenant_ID_Here
durch Ihre Mandanten-ID. Wenn Sie Ihre Mandanten-ID nicht kennen, erfahren Sie hier, wie Sie die Mandantendetails abrufen. - Fügen Sie die
knownAuthorities
-Eigenschaft mit dem Wert [Enter_the_Custom_Domain_Here] hinzu.
- Aktualisieren Sie den Wert der
Nachdem Sie die Änderungen an der Datei appsettings.json vorgenommen haben, sollte die Datei in etwa wie der folgende Codeschnipsel aussehen (wenn Ihre benutzerdefinierte Domänen-URL login.contoso.com lautet und Ihre Mandanten-ID aaaabbbb-0000-cccc-1111-dddd2222eeee ist):
{
"AzureAd": {
"Instance": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
"KnownAuthorities": ["login.contoso.com"]
},
"Logging": {...},
"AllowedHosts": "*"
}
Hinzufügen von Anwendungsrolle und -Bereich
Alle APIs müssen mindestens einen Bereich (auch „delegierte Berechtigung“ genannt) veröffentlichen, damit die Clientanwendungen erfolgreich ein Zugriffstoken für Benutzer*innen abrufen können. APIs sollten ebenfalls mindestens eine App-Rolle für Anwendungen (auch „Anwendungsberechtigung“ genannt) veröffentlichen, damit die Clientanwendungen im eigenen Namen ein Zugriffstoken abrufen können, d. h., wenn kein*e Benutzer*in angemeldet wird.
Wir geben diese Berechtigungen in der Datei appsettings.json an. Für dieses Tutorials haben wir vier Berechtigungen registriert. ToDoList.ReadWrite und ToDoList.Read delegierte Berechtigungen sowie ToDoList.ReadWrite.All und ToDoList.Read.All als Anwendungsberechtigungen.
{
"AzureAd": {
"Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
"Scopes": {
"Read": ["ToDoList.Read", "ToDoList.ReadWrite"],
"Write": ["ToDoList.ReadWrite"]
},
"AppPermissions": {
"Read": ["ToDoList.Read.All", "ToDoList.ReadWrite.All"],
"Write": ["ToDoList.ReadWrite.All"]
}
},
"Logging": {...},
"AllowedHosts": "*"
}
Hinzufügen eines Authentifizierungsschemas
Wenn der Authentifizierungsdienst während der Authentifizierung konfiguriert wird, wird ein Authentifizierungsschema benannt. In diesem Artikel wird das JWT-Bearerauthentifizierungsschema verwendet. Fügen Sie den folgenden Code in der Datei Programs.cs hinzu, um das Authentifizierungsschema hinzuzufügen.
// Add the following to your imports
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
// Add authentication scheme
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration);
Erstellen Ihrer Modelle
Erstellen Sie einen Ordner namens Models im Stammordner Ihres Projekts. Erstellen Sie in diesem Ordner eine Datei namens ToDo.cs, und fügen Sie den folgenden Code hinzu. Dieser Code erstellt ein Modell namens ToDo.
using System;
namespace ToDoListAPI.Models;
public class ToDo
{
public int Id { get; set; }
public Guid Owner { get; set; }
public string Description { get; set; } = string.Empty;
}
Hinzufügen eines Datenbankkontexts
Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableitung von der Microsoft.EntityFrameworkCore.DbContext-Klasse-Klasse erstellt. In diesem Tutorial verwenden wir einen IMDB-Katalog zu Testzwecken.
Erstellen Sie im Stammordner des Projekts einen Ordner namens DbContext.
Erstellen Sie in diesem Ordner eine Datei namens ToDoContext.cs, und fügen Sie der Datei dann den folgenden Inhalt hinzu:
using Microsoft.EntityFrameworkCore; using ToDoListAPI.Models; namespace ToDoListAPI.Context; public class ToDoContext : DbContext { public ToDoContext(DbContextOptions<ToDoContext> options) : base(options) { } public DbSet<ToDo> ToDos { get; set; } }
Öffnen Sie die Datei Program.cs im Stammordner Ihrer App, und fügen Sie dann den folgenden Code in der Datei hinzu. Mit diesem Code wird eine
DbContext
-Unterklasse namensToDoContext
als bereichsbezogener Dienst im ASP.NET Core-Anwendungsdienstanbieter registriert (auch als Container für Abhängigkeitsinjektion bezeichnet). Der Kontext ist für die Verwendung des IMDB-Katalogs konfiguriert.// Add the following to your imports using ToDoListAPI.Context; using Microsoft.EntityFrameworkCore; builder.Services.AddDbContext<ToDoContext>(opt => opt.UseInMemoryDatabase("ToDos"));
Hinzufügen von Controllern
In den meisten Fällen verfügt der Controller über mehrere Aktionen. In der Regel sind dies CRUD-Aktionen (Create, Read, Update, Delete, d. h. Erstellen, Lesen, Aktualisieren, Löschen). In diesem Tutorial erstellen wir nur zwei Aktionselemente. Ein Aktionselement zum Lesen aller Elemente und ein Aktionselement zum Erstellen, um zu veranschaulichen, wie Sie Ihre Endpunkte schützen.
Navigieren Sie zum Ordner Controller im Stammordner Ihres Projekts.
Erstellen Sie in diesem Ordner eine Datei namens ToDoListController.cs. Öffnen Sie die Datei, und fügen Sie dann die folgenden Codebausteine hinzu:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Web; using Microsoft.Identity.Web.Resource; using ToDoListAPI.Models; using ToDoListAPI.Context; namespace ToDoListAPI.Controllers; [Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController : ControllerBase { private readonly ToDoContext _toDoContext; public ToDoListController(ToDoContext toDoContext) { _toDoContext = toDoContext; } [HttpGet()] [RequiredScopeOrAppPermission()] public async Task<IActionResult> GetAsync(){...} [HttpPost] [RequiredScopeOrAppPermission()] public async Task<IActionResult> PostAsync([FromBody] ToDo toDo){...} private bool RequestCanAccessToDo(Guid userId){...} private Guid GetUserId(){...} private bool IsAppMakingRequest(){...} }
Hinzufügen von Code zum Controller
In diesem Abschnitt fügen wir den von uns erstellten Platzhaltern Code hinzu. Hier geht es nicht darum, die API zu erstellen, sondern sie zu schützen.
Importieren Sie die erforderlichen Pakete. Das Paket Microsoft.Identity.Web ist ein MSAL-Wrapper, der uns hilft, die Authentifizierungslogik einfach zu handhaben, z. B. mithilfe der Tokenüberprüfung. Um sicherzustellen, dass unsere Endpunkte eine Autorisierung benötigen, verwenden wir das integrierte Paket Microsoft.AspNetCore.Authorization.
Da wir die Berechtigungen für den Aufruf dieser API entweder mithilfe von delegierten Berechtigungen im Namen des Benutzers oder mithilfe von Anwendungsberechtigungen erteilt haben, bei denen der Aufruf durch den Client selbst und nicht im Namen des Benutzers erfolgt, ist es wichtig zu wissen, ob der Aufruf von der App in ihrem eigenen Namen erfolgt. Die Ansprüche stellen die einfachste Methode dar. So kann ermittelt werden, ob das Zugriffstoken den optionalen Anspruch
idtyp
enthält. Der Anspruchidtyp
ist der einfachste Weg, wie die API feststellen kann, ob ein Token ein App-Token oder ein App- und Benutzertoken ist. Es wird empfohlen, den optionalen Anspruchidtyp
zu aktivieren.Wenn der Anspruch
idtyp
nicht aktiviert ist, können Sie die Ansprücheroles
undscp
verwenden, um zu bestimmen, ob es sich bei dem Zugriffstoken um ein App-Token oder um ein App- und Benutzertoken handelt. Ein von Microsoft Entra External ID ausgestelltes Zugriffstoken enthält mindestens einen der beiden Ansprüche. Für Benutzer*innen ausgestellte Zugriffstoken verfügen über den Anspruchscp
. Für eine Anwendung ausgestellte Zugriffstoken verfügen über den Anspruchroles
. Zugriffstoken, die beide Ansprüche enthalten, werden nur für Benutzer*innen ausgestellt, wobei der Anspruchscp
die delegierten Berechtigungen festlegt, während der Anspruchroles
die Rolle des Benutzers oder der Benutzerin festlegt. Zugriffstoken, die keinen der beiden Ansprüche enthalten, sind nicht zu berücksichtigen.private bool IsAppMakingRequest() { if (HttpContext.User.Claims.Any(c => c.Type == "idtyp")) { return HttpContext.User.Claims.Any(c => c.Type == "idtyp" && c.Value == "app"); } else { return HttpContext.User.Claims.Any(c => c.Type == "roles") && !HttpContext.User.Claims.Any(c => c.Type == "scp"); } }
Fügen Sie eine Hilfsfunktion hinzu, die feststellt, ob die gestellte Anforderung genügend Berechtigungen enthält, um die beabsichtigte Aktion auszuführen. Überprüfen Sie, ob die App die Anforderung im eigenen Namen stellt oder ob die App den Aufruf im Namen eines Benutzers oder einer Benutzerin tätigt, der*die Besitzer*in der betreffenden Ressource ist, indem wir die Benutzer-ID überprüfen.
private bool RequestCanAccessToDo(Guid userId) { return IsAppMakingRequest() || (userId == GetUserId()); } private Guid GetUserId() { Guid userId; if (!Guid.TryParse(HttpContext.User.GetObjectId(), out userId)) { throw new Exception("User ID is not valid."); } return userId; }
Fügen Sie die Berechtigungsdefinitionen ein, um Routen zu schützen. Schützen Sie Ihre API, indem Sie das Attribut
[Authorize]
zur Controllerklasse hinzufügen. Dadurch wird sichergestellt, dass die Controlleraktionen nur aufgerufen werden können, wenn die API mit einer autorisierten Identität aufgerufen wird. Die Berechtigungsdefinitionen legen fest, welche Arten von Berechtigungen zur Durchführung dieser Aktionen erforderlich sind.[Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController: ControllerBase{...}
Fügen Sie Berechtigungen für den GET all-Endpunkt und den POST-Endpunkt hinzu. Dazu verwenden Sie die Methode RequiredScopeOrAppPermission, die Teil des Namespace Microsoft.Identity.Web.Resource ist. Anschließend übergeben Sie Bereiche und Berechtigungen über die Attribute RequiredScopesConfigurationKey und RequiredAppPermissionsConfigurationKey an diese Methode.
[HttpGet] [RequiredScopeOrAppPermission( RequiredScopesConfigurationKey = "AzureAD:Scopes:Read", RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Read" )] public async Task<IActionResult> GetAsync() { var toDos = await _toDoContext.ToDos! .Where(td => RequestCanAccessToDo(td.Owner)) .ToListAsync(); return Ok(toDos); } [HttpPost] [RequiredScopeOrAppPermission( RequiredScopesConfigurationKey = "AzureAD:Scopes:Write", RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Write" )] public async Task<IActionResult> PostAsync([FromBody] ToDo toDo) { // Only let applications with global to-do access set the user ID or to-do's var ownerIdOfTodo = IsAppMakingRequest() ? toDo.Owner : GetUserId(); var newToDo = new ToDo() { Owner = ownerIdOfTodo, Description = toDo.Description }; await _toDoContext.ToDos!.AddAsync(newToDo); await _toDoContext.SaveChangesAsync(); return Created($"/todo/{newToDo!.Id}", newToDo); }
Ausführen Ihrer API
Führen Sie Ihre API aus, um sicherzustellen, dass sie mit dem Befehl dotnet run
fehlerfrei ausgeführt wird. Wenn Sie das HTTPS-Protokoll auch während der Testphase verwenden möchten, müssen Sie dem Entwicklungszertifikat von .NET vertrauen.
Ein vollständiges Beispiel für diesen API-Code finden Sie in der Beispieldatei.