Implementieren Sie die Autorisierung in Web-APIs mit Microsoft. Identity.Web

In diesem Artikel implementieren Sie die Autorisierung in ASP.NET Core Web-APIs mithilfe von Microsoft. Identity.Web. Sie überprüfen Bereiche (delegierte Berechtigungen) und App-Berechtigungen (Anwendungsberechtigungen), um den Zugriff auf geschützte Ressourcen zu steuern. In den Beispielen wird Microsoft Entra ID als Identitätsanbieter verwendet.

Grundlegendes zu Autorisierungskonzepten

In diesem Abschnitt werden die wichtigsten Unterschiede zwischen Authentifizierung und Autorisierung behandelt und beschrieben, was Microsoft. Identity.Web überprüft in Zugriffstoken.

Authentifizierung und Autorisierung

Konzept Purpose Ergebnis
Authentifizierung Identität überprüfen 401 Nicht autorisiert, wenn ein Fehler auftritt
Autorisierung Überprüfen von Berechtigungen 403 Verboten, wenn nicht ausreichend

Was überprüft wird

Wenn eine Web-API ein Zugriffstoken empfängt, Microsoft. Identity.Web überprüft:

  1. Tokensignatur – Stammt sie von einer vertrauenswürdigen Autorität?
  2. Tokengruppe – Ist sie für diese API vorgesehen?
  3. Tokenablauf – Ist es noch gültig?
  4. Bereiche/Rollen – Verfügen die Client-App und das Subjekt (Benutzer) über die richtigen Berechtigungen?

Dieser Leitfaden konzentriert sich auf #4 – Überprüfen von Bereichen und App-Berechtigungen.

Bereiche (delegierte Berechtigungen)

Bereiche gelten, wenn ein Benutzer die Berechtigung an eine App delegiert, damit diese im Auftrag des Benutzers handelt (zum Beispiel eine Web-API, die im Auftrag eines angemeldeten Benutzers aufgerufen wird).

Einzelheit Wert
Tokenanforderung scp oder scope (Client-App); roles (Benutzer)
Beispielwerte "access_as_user", "User.Read""Files.ReadWrite"

App-Berechtigungen (Anwendungsberechtigungen)

App-Berechtigungen gelten, wenn eine App die Web-API ohne Benutzerkontext aufruft, z. B. einen Daemon oder hintergrunddienst mit Clientanmeldeinformationen.

Einzelheit Wert
Tokenanspruch roles
Beispielwerte "Mail.Read.All", "User.Read.All"

Überprüfen von Anwendungsbereichen mit RequiredScope

Das RequiredScope Attribut überprüft, ob das Zugriffstoken mindestens einen der angegebenen Bereiche enthält. Verwenden Sie dieses Attribut, wenn Ihre API nur benutzerdelegierte Anforderungen erfüllt.

Einrichten der Bereichsüberprüfung

Führen Sie die folgenden Schritte aus, um die Bereichsüberprüfung in Ihrer API zu aktivieren.

1. Aktivieren Sie die Autorisierung in Ihrer API:

Fügen Sie Ihrer Anwendungspipeline Authentifizierungs- und Autorisierungsdienste hinzu:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization(); // Required for authorization

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization(); // Must be after UseAuthentication
app.MapControllers();

app.Run();

2. Schützen von Controllern oder Aktionen:

Wenden Sie die [Authorize]- und [RequiredScope]-Attribute auf Ihren Controller oder einzelne Aktionen an:

using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;

[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
    [HttpGet]
    public IActionResult GetTodos()
    {
        // Only accessible if token has "access_as_user" scope
        return Ok(new[] { "Todo 1", "Todo 2" });
    }
}

Anwenden von Bereichsmustern

Wählen Sie das Muster aus, das am besten dazu passt, wie Sie Bereiche in Ihrer Anwendung verwalten.

Muster 1: Hartcodierte Bereiche

Verwenden Sie dieses Muster, wenn die Gültigkeitsbereiche zum Zeitpunkt der Entwicklung festgelegt und bekannt sind.

[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
    // All actions require "access_as_user" scope
}

Um einen von mehreren Bereichen zu akzeptieren, listen Sie sie als Parameter auf:

[Authorize]
[RequiredScope("read", "write", "admin")]
public class TodoListController : ControllerBase
{
    // Token must have "read" OR "write" OR "admin"
}

Muster 2: Gültigkeitsbereiche aus der Konfiguration

Verwenden Sie dieses Muster, wenn Bereiche pro Umgebung konfigurierbar sein sollen. Definieren Sie die Bereiche in Ihrer Konfigurationsdatei:

appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-api-client-id",
    "Scopes": "access_as_user read write"
  }
}

Verweisen Sie auf den Konfigurationsschlüssel in Ihrem Controller:

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : ControllerBase
{
    // Scopes read from configuration
}

Mit diesem Ansatz können Sie Bereiche ändern, ohne sie neu zu kompilieren.

Muster 3: Bereiche auf Aktionsebene

Verwenden Sie dieses Muster, wenn unterschiedliche Aktionen unterschiedliche Berechtigungen erfordern. Wenden Sie [RequiredScope] auf einzelne Aktionsmethoden an:

[Authorize]
public class TodoListController : ControllerBase
{
    [HttpGet]
    [RequiredScope("read")]
    public IActionResult GetTodos()
    {
        return Ok(todos);
    }

    [HttpPost]
    [RequiredScope("write")]
    public IActionResult CreateTodo([FromBody] Todo todo)
    {
        // Only tokens with "write" scope can create
        return CreatedAtAction(nameof(GetTodos), todo);
    }

    [HttpDelete("{id}")]
    [RequiredScope("admin")]
    public IActionResult DeleteTodo(int id)
    {
        // Only tokens with "admin" scope can delete
        return NoContent();
    }
}

Grundlegendes zum Validierungsablauf

Wenn eine Anforderung eingeht, verarbeitet die Middleware sie in der folgenden Reihenfolge:

  1. ASP.NET Core Authentifizierungs-Middleware überprüft das Token
  2. RequiredScope Attributüberprüfungen für den scp- oder scope-Anspruch
  3. Wenn das Token mindestens einen übereinstimmenden Bereich enthält, wird die Anforderung fortgesetzt.
  4. Wenn kein übereinstimmenden Bereich gefunden wird, gibt die API eine 403 Verbotene Antwort zurück.

Das folgende Beispiel zeigt eine typische Fehlerantwort:

{
  "error": "insufficient_scope",
  "error_description": "The token does not have the required scope 'access_as_user'."
}

Überprüfen von App-Berechtigungen mit RequiredScopeOrAppPermission

Das RequiredScopeOrAppPermission Attribut überprüft entweder Bereiche (delegiert) oder App-Berechtigungen (Anwendung). Verwenden Sie dieses Attribut, wenn Ihre API sowohl benutzerdelegierte Apps als auch Daemon-/Dienst-Apps vom selben Endpunkt aus bereitstellt.

Wenn Ihre API nur benutzerdelegierte Anforderungen bedient, verwenden Sie RequiredScope stattdessen.

Einrichten von Bereichs- oder App-Berechtigungsüberprüfungen

Wenden Sie das Attribut an, um einen der Tokentypen zu akzeptieren:

using Microsoft.Identity.Web.Resource;

[Authorize]
[RequiredScopeOrAppPermission(
    AcceptedScope = new[] { "access_as_user" },
    AcceptedAppPermission = new[] { "TodoList.ReadWrite.All" }
)]
public class TodoListController : ControllerBase
{
    [HttpGet]
    public IActionResult GetTodos()
    {
        // Accessible with EITHER:
        // - User-delegated token with "access_as_user" scope, OR
        // - App-only token with "TodoList.ReadWrite.All" app permission
        return Ok(todos);
    }
}

Konfigurieren von App-Berechtigungen aus Einstellungen

Speicherbereiche und App-Berechtigungen in der Konfiguration speichern, um sie ohne erneutes Kompilieren zu ändern.

appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-api-client-id",
    "Scopes": "access_as_user",
    "AppPermissions": "TodoList.ReadWrite.All TodoList.Admin"
  }
}

Verweisen Sie auf die Konfigurationsschlüssel in Ihrem Controller:

[Authorize]
[RequiredScopeOrAppPermission(
    RequiredScopesConfigurationKey = "AzureAd:Scopes",
    RequiredAppPermissionsConfigurationKey = "AzureAd:AppPermissions"
)]
public class TodoListController : ControllerBase
{
    // Scopes and app permissions from configuration
}

Unterschiede bei Tokenansprüchen vergleichen

In der folgenden Tabelle wird gezeigt, wie sich Ansprüche zwischen benutzerdelegierten und Nur-App-Token unterscheiden.

Tokentyp Anspruch Beispielwert
Benutzerdelegiert scp oder scope "access_as_user User.Read"
Nur in der App roles ["TodoList.ReadWrite.All"]

Das folgende Beispiel zeigt ein vom Benutzer delegiertes Token:

{
  "aud": "api://your-api-client-id",
  "iss": "https://login.microsoftonline.com/.../v2.0",
  "scp": "access_as_user",
  "sub": "user-object-id",
  ...
}

Das folgende Beispiel zeigt ein Nur-App-Token:

{
  "aud": "api://your-api-client-id",
  "iss": "https://login.microsoftonline.com/.../v2.0",
  "roles": ["TodoList.ReadWrite.All"],
  "sub": "app-object-id",
  ...
}

Erstellen von Autorisierungsrichtlinien

Verwenden Sie für komplexe Autorisierungsszenarien ASP.NET Core Autorisierungsrichtlinien. Mithilfe von Richtlinien können Sie Regeln zentralisieren, mehrere Anforderungen kombinieren und testbare Autorisierungslogik schreiben.

Benefit Beschreibung
Zentrale Logik Definieren von Autorisierungsregeln einmal, überall wiederverwenden
Komponierbar Kombinieren mehrerer Anforderungen (Bereiche + Ansprüche + benutzerdefinierte Logik)
Testbar Einfachere Komponententestautorisierungslogik
Flexibel Benutzerdefinierte Anforderungen über die Gültigkeitsprüfung hinaus

Muster 1: Definieren einer Richtlinie mit RequireScope

Definieren Sie benannte Richtlinien, die bestimmte Bereiche erfordern, und verweisen Sie dann auf ihre Controller:

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("TodoReadPolicy", policyBuilder =>
    {
        policyBuilder.RequireScope("read", "access_as_user");
    });

    options.AddPolicy("TodoWritePolicy", policyBuilder =>
    {
        policyBuilder.RequireScope("write", "admin");
    });
});

var app = builder.Build();

Wenden Sie die Richtlinien auf Controlleraktionen an:

[Authorize]
public class TodoListController : ControllerBase
{
    [HttpGet]
    [Authorize(Policy = "TodoReadPolicy")]
    public IActionResult GetTodos()
    {
        return Ok(todos);
    }

    [HttpPost]
    [Authorize(Policy = "TodoWritePolicy")]
    public IActionResult CreateTodo([FromBody] Todo todo)
    {
        return CreatedAtAction(nameof(GetTodos), todo);
    }
}

Muster 2: Definieren einer Richtlinie mit ScopeAuthorizationRequirement

Verwendung ScopeAuthorizationRequirement für explizitere Umfangsanforderungen:

using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CustomPolicy", policyBuilder =>
    {
        policyBuilder.AddRequirements(
            new ScopeAuthorizationRequirement(new[] { "access_as_user" })
        );
    });
});

Muster 3: Festlegen einer Standardrichtlinie

Legen Sie eine Standardrichtlinie fest, die für alle [Authorize] Attribute automatisch gilt:

builder.Services.AddAuthorization(options =>
{
    var defaultPolicy = new AuthorizationPolicyBuilder()
        .RequireScope("access_as_user")
        .Build();

    options.DefaultPolicy = defaultPolicy;
});

Für jedes [Authorize] Attribut ist jetzt der access_as_user Bereich erforderlich:

[Authorize] // Automatically requires "access_as_user" scope
public class TodoListController : ControllerBase
{
    // All actions protected by default policy
}

Muster 4: Kombinieren mehrerer Anforderungen

Kombinieren Sie Bereichs-, Rollen- und Authentifizierungsanforderungen in einer einzigen Richtlinie:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminPolicy", policyBuilder =>
    {
        policyBuilder.RequireScope("admin");
        policyBuilder.RequireRole("Admin"); // Also check role claim
        policyBuilder.RequireAuthenticatedUser();
    });
});

Muster 5: Erstellen einer Richtlinie aus der Konfiguration

Laden Sie Geltungsbereiche aus der Konfiguration, um Richtlinien umgebungsspezifisch zu halten.

var requiredScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ');

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ApiAccessPolicy", policyBuilder =>
    {
        if (requiredScopes != null)
        {
            policyBuilder.RequireScope(requiredScopes);
        }
    });
});

Filtern von Anforderungen nach Mandant

Einschränken des API-Zugriffs auf Token von bestimmten Microsoft Entra Mandanten. Dies ist nützlich, wenn Ihre Multi-Tenant-API nur Anfragen von autorisierten Kundenmandanten annehmen sollte.

Einschränken des Zugriffs auf zulässige Mandanten

Definieren Sie eine Richtlinie, die den Mandanten-ID-Anspruch gegen die Erlaubnisliste überprüft.

builder.Services.AddAuthorization(options =>
{
    string[] allowedTenants =
    {
        "14c2f153-90a7-4689-9db7-9543bf084dad", // Contoso tenant
        "af8cc1a0-d2aa-4ca7-b829-00d361edb652", // Fabrikam tenant
        "979f4440-75dc-4664-b2e1-2cafa0ac67d1"  // Northwind tenant
    };

    options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
    {
        policyBuilder.RequireClaim(
            "http://schemas.microsoft.com/identity/claims/tenantid",
            allowedTenants
        );
    });

    // Apply to all endpoints by default
    options.DefaultPolicy = options.GetPolicy("AllowedTenantsOnly");
});

Konfigurieren Sie die Mandantenfilterung über die Einstellungen

Speichern Sie zulässige Mandanten-IDs in der Konfiguration, um sie ohne Änderungen am Code verwalten zu können.

appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "ClientId": "your-api-client-id",
    "AllowedTenants": [
      "14c2f153-90a7-4689-9db7-9543bf084dad",
      "af8cc1a0-d2aa-4ca7-b829-00d361edb652"
    ]
  }
}

Lesen Sie die Mandantenliste, und erstellen Sie die Richtlinie beim Start:

var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants")
    .Get<string[]>();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
    {
        policyBuilder.RequireClaim(
            "http://schemas.microsoft.com/identity/claims/tenantid",
            allowedTenants ?? Array.Empty<string>()
        );
    });
});

Kombinieren von Geltungsbereichen mit Mandantenfilterung

Erstellen Sie eine Richtlinie, die sowohl einen gültigen Bereich als auch einen genehmigten Mandanten erfordert:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("SecureApiAccess", policyBuilder =>
    {
        // Require specific scope
        policyBuilder.RequireScope("access_as_user");

        // AND require specific tenant
        policyBuilder.RequireClaim(
            "http://schemas.microsoft.com/identity/claims/tenantid",
            allowedTenants
        );
    });
});

Bewährte Methoden befolgen

Wenden Sie diese Empfehlungen an, um eine sichere, verwaltete Autorisierungslogik zu erstellen.

Empfohlen

1. Paaren Sie [Authorize] immer mit einer Bereichsüberprüfung:

[Authorize] // Authentication
[RequiredScope("access_as_user")] // Authorization
public class MyController : ControllerBase { }

2. Verwenden Sie die Konfiguration für umgebungsspezifische Bereiche:

[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]

3. Anwenden der geringsten Rechte:

[HttpGet]
[RequiredScope("read")] // Only read permission needed

[HttpPost]
[RequiredScope("write")] // Write permission for modifications

4. Verwenden von Richtlinien für komplexe Autorisierung:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
    {
        policy.RequireScope("admin");
        policy.RequireClaim("department", "IT");
    });
});

5. Detaillierte Fehlerantworten in der Entwicklung aktivieren:

if (builder.Environment.IsDevelopment())
{
    Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}

DONT‘s

1. Überspringen Sie [Authorize] nicht, wenn Sie RequiredScope verwenden:

//  Wrong - RequiredScope won't work without [Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }

//  Correct
[Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }

2. Codieren Sie keine Mandanten-IDs in der Produktion:

//  Wrong
policyBuilder.RequireClaim("tid", "14c2f153-90a7-4689-9db7-9543bf084dad");

//  Better - use configuration
var tenants = Configuration.GetSection("AllowedTenants").Get<string[]>();
policyBuilder.RequireClaim("tid", tenants);

3. Verwechseln Sie Bereiche nicht mit Rollen:

//  Wrong - This checks roles claim, not scopes
[RequiredScope("Admin")] // "Admin" is typically a role, not a scope

//  Correct
[RequiredScope("access_as_user")] // Scope
[Authorize(Roles = "Admin")] // Role

4. Keine vertraulichen Bereichsinformationen in Produktionsfehlermeldungen verfügbar machen:

Konfigurieren Sie die entsprechenden Protokollierungsstufen und die Fehlerbehandlung für Produktionsumgebungen.


Behebung von Autorisierungsproblemen

Verwenden Sie die folgenden Anleitungen, um häufige Autorisierungsprobleme zu diagnostizieren.

403 Verboten - fehlender Bereich

Fehler: DIE API gibt 403 auch mit einem gültigen Token zurück.

Diagnose:

  1. Decodieren Sie das Token bei jwt.ms.
  2. Überprüfen Sie den Anspruch für scp oder scope.
  3. Überprüfen Sie, ob der Wert Ihrem RequiredScope Attribut entspricht.

Lösung:

  • Stellen Sie sicher, dass die Client-App den richtigen Scope anfordert, wenn sie das Token erwirbt.
  • Überprüfen Sie, ob der Bereich in der API-App-Registrierung in Microsoft Entra verfügbar gemacht wird.
  • Erteilen Sie ggf. die Admin-Zustimmung.

RequiredScope funktioniert nicht

Symptom: Das Attribut wird ignoriert.

Prüfen:

  1. Haben Sie das [Authorize] Attribut hinzugefügt?
  2. Wird app.UseAuthorization() nach app.UseAuthentication() aufgerufen?
  3. Ist services.AddAuthorization() registriert?

Konfigurationsschlüssel nicht gefunden

Fehler: Die Bereichsüberprüfung schlägt lautlos fehl.

Prüfen:

{
  "AzureAd": {
    "Scopes": "access_as_user" // Matches RequiredScopesConfigurationKey
  }
}

Stellen Sie sicher, dass der Konfigurationspfad exakt übereinstimmt.