Implementace autorizace ve webových rozhraních API pomocí Microsoft Identity.Web

V tomto článku implementujete autorizaci ve webových rozhraních API ASP.NET Core pomocí Microsoft. Identity.Web. Ověříte obory (delegovaná oprávnění) a oprávnění aplikace (oprávnění aplikace) k řízení přístupu k chráněným prostředkům. Příklady používají Microsoft Entra ID jako zprostředkovatele identity.

Vysvětlení konceptů autorizace

Tato část popisuje klíčové rozdíly mezi ověřováním a autorizací a popisuje, co Microsoft. Identity.Web ověřuje v přístupových tokenech.

Ověřování vs. autorizace

Koncepce Purpose Výsledek
Autentizace Ověření identity 401 Neautorizovaný přístup v případě selhání
Authorization Kontrola oprávnění 403 Zakázáno, pokud je nedostatečné

Co se ověří

Když webové rozhraní API obdrží přístupový token, Microsoft. Identity.Web ověřuje:

  1. Podpis tokenu – Pochází z důvěryhodné autority?
  2. Cílová skupina tokenů – Je určená pro toto rozhraní API?
  3. Vypršení platnosti tokenu – je stále platný?
  4. Rozsahy/Role – Mají klientská aplikace a subjekt (uživatel) potřebná oprávnění?

Tato příručka se zaměřuje na #4 – ověřování oborů a oprávnění aplikací.

Obory (delegovaná oprávnění)

Rozsahy platí, když uživatel deleguje oprávnění na aplikaci, aby jednal jejich jménem (například webové API volané na základě oprávnění přihlášeného uživatele).

Podrobnost Hodnota
Požadavek na token scp nebo scope (klientská aplikace); roles (uživatel)
Ukázkové hodnoty "access_as_user", "User.Read", "Files.ReadWrite"

Oprávnění aplikací (přístupová práva aplikace)

Oprávnění aplikace se použijí, když aplikace volá webové rozhraní API sama o sobě bez kontextu uživatele, například jako démon nebo služba na pozadí používající přihlašovací údaje klienta.

Podrobnost Hodnota
Nárok tokenu roles
Ukázkové hodnoty "Mail.Read.All", "User.Read.All"

Ověření oborů pomocí RequiredScope

Atribut RequiredScope zkontroluje, že přístupový token obsahuje alespoň jeden ze zadaných oborů. Tento atribut použijte, když vaše rozhraní API obsluhuje pouze požadavky delegované uživatelem.

Nastavení ověřování oboru

Pokud chcete ve svém rozhraní API povolit ověřování oboru, postupujte podle těchto kroků.

1. Povolení autorizace v rozhraní API:

Přidání ověřovacích a autorizačních služeb do kanálu aplikace:

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. Ochrana kontrolerů nebo akcí:

Aplikujte atributy [Authorize] a [RequiredScope] k vašemu kontroleru nebo jednotlivým akcím:

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" });
    }
}

Použijte vzory oboru

Zvolte vzor, který nejlépe vyhovuje způsobu správy oborů ve vaší aplikaci.

Vzor 1: Pevně zakódované obory

Tento vzor použijte, pokud jsou obory předem stanovené a známé během vývoje.

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

Pokud chcete přijmout některý z více oborů, uveďte je jako parametry:

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

Vzorec 2: Rozsahy z konfigurace

Tento vzor použijte, pokud mají být obory konfigurovatelné pro každé prostředí. Definujte obory v konfiguračním souboru:

appsettings.json:

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

Odkaz na konfigurační klíč v kontroleru:

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

Tento přístup umožňuje změnit rozsahy bez rekompilování.

Vzor 3: Rozsahy na úrovni akce

Tento vzor použijte, když různé akce vyžadují různá oprávnění. Použijte [RequiredScope] na jednotlivé metody akcí:

[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();
    }
}

Vysvětlení toku ověřování

Jakmile požadavek dorazí, middleware ho zpracuje v následujícím pořadí:

  1. ASP.NET Core ověřovací middleware validuje token.
  2. RequiredScope kontrola atributu scp nebo scope nároku
  3. Pokud token obsahuje alespoň jeden odpovídající obor, požadavek pokračuje.
  4. Pokud se nenajde žádný odpovídající obor, rozhraní API vrátí odpověď 403 Zakázáno.

Následující příklad ukazuje typickou chybovou odpověď:

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

Ověření oprávnění aplikace pomocí RequiredScopeOrAppPermission

Atribut RequiredScopeOrAppPermission ověří buď obory (delegované), nebo oprávnění aplikace (aplikace). Tento atribut použijte, když vaše rozhraní API obsluhuje aplikace delegované uživatelem i aplikace démona nebo služby ze stejného koncového bodu.

Pokud vaše rozhraní API obsluhuje pouze požadavky delegované uživatelem, použijte RequiredScope místo toho.

Nastavení ověření oboru nebo oprávnění aplikace

Pomocí atributu přijměte jeden typ tokenu:

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);
    }
}

Konfigurace oprávnění aplikace z nastavení

Ukládejte obory a oprávnění aplikace v konfiguraci, abyste je mohli změnit bez rekompilace.

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"
  }
}

Odkazujte na konfigurační klíče v kontroleru:

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

Porovnání rozdílů v nárocích tokenů

Následující tabulka ukazuje, jak se nároky liší mezi tokeny delegovanými uživatelem a tokeny pouze pro aplikace.

Typ tokenu Požadavek Příklad hodnoty
Delegované uživatelem scp nebo scope "access_as_user User.Read"
Pouze pro aplikaci roles ["TodoList.ReadWrite.All"]

Následující příklad ukazuje token delegovaný uživatelem:

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

Následující příklad ukazuje token jen pro aplikaci:

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

Vytvoření zásad autorizace

V případě složitých scénářů autorizace použijte zásady autorizace ASP.NET Core. Zásady umožňují centralizovat pravidla, kombinovat více požadavků a zapisovat testovatelnou autorizační logiku.

Benefit Description
Centralizovaná logika Definování autorizačních pravidel jednou, opakované použití všude
Skládatelný Kombinování více požadavků (rozsahy + nároky + vlastní logika)
Testovatelné Jednodušší logika autorizace pro testování jednotek
Ohebný Vlastní požadavky nad rámec ověřování rozsahu

Model 1: Definování zásad pomocí RequireScope

Definujte pojmenované zásady, které vyžadují konkrétní obory, a pak na ně odkazujte na kontrolery:

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();

Použijte zásady na akce kontroleru:

[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);
    }
}

Model 2: Definování zásady pomocí ScopeAuthorizationRequirement

Použijte ScopeAuthorizationRequirement pro explicitnější požadavky na rozsah:

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" })
        );
    });
});

Model 3: Nastavení výchozí zásady

Nastavte výchozí zásadu, která se vztahuje na všechny [Authorize] atributy automaticky:

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

    options.DefaultPolicy = defaultPolicy;
});

Každý [Authorize] atribut teď vyžaduje access_as_user obor:

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

Model 4: Kombinování více požadavků

Zkombinujte požadavky na obor, roli a ověřování v jedné zásadě:

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

Model 5: Sestavení zásady z konfigurace

Rozsahy zatížení z konfigurace za účelem zachování zásad specifických pro prostředí:

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

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

Filtrování požadavků podle tenanta

Omezte přístup rozhraní API k tokenům z konkrétních Microsoft Entra tenantů. To je užitečné, když vaše víceklientské rozhraní API by mělo přijímat jenom žádosti od schválených tenantů zákazníků.

Omezení přístupu k povoleným tenantům

Definujte zásadu, která kontroluje deklaraci identity ID tenanta proti seznamu povolených:

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");
});

Konfigurace filtrování nájemce v nastavení

Uložte povolené ID tenanta v konfiguraci, abyste je mohli spravovat beze změn kódu.

appsettings.json:

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

Přečtěte si seznam tenantů a vytvořte zásadu při spuštění:

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>()
        );
    });
});

Kombinování oborů s filtrováním tenanta

Vytvořte zásadu, která vyžaduje platný obor i schváleného tenanta:

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
        );
    });
});

Dodržujte osvědčené postupy.

Tato doporučení použijte k vytvoření zabezpečené a udržovatelné autorizační logiky.

Co dělat

1. Vždy spárujte [Authorize] s validací oboru:

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

2. Použijte konfiguraci pro obory specifické pro prostředí:

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

3. Použijte nejnižší oprávnění:

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

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

4. Použijte zásady pro komplexní autorizaci:

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

5. Povolte podrobné odpovědi na chyby ve vývoji:

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

Zakázané činnosti

1. Nevynechávejte [Authorize] při použití RequiredScope:

//  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. Nezakódujte PEVNĚ ID tenanta v produkčním prostředí:

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

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

3. Nezaměňujte obory s rolemi:

//  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. Nezpřístupňujte citlivé informace o rozsahu v chybových zprávách v produkčním prostředí:

Nakonfigurujte odpovídající úrovně protokolování a zpracování chyb pro produkční prostředí.


Řešení potíží s autorizací

Při diagnostice běžných problémů s autorizací využijte následující doprovodné materiály.

403 Zakázáno – chybějící obor

Chyba: API vrací kód 403, i když je použit platný token.

Diagnóza:

  1. Dekódujte token na jwt.ms.
  2. Zkontrolujte scpnebo scope nárok.
  3. Ověřte, že hodnota odpovídá vašemu atributu RequiredScope .

Solution:

  • Ujistěte se, že klientská aplikace při získávání tokenu požaduje správný obor.
  • Ověřte, že je rozsah zveřejněn v registraci API aplikace v Microsoft Entra.
  • V případě potřeby udělte souhlas správce.

RequiredScope nefunguje

Příznakem: Atribut se zdá být ignorován.

Kontrola:

  1. Přidali jste [Authorize] atribut?
  2. Je app.UseAuthorization() volána po app.UseAuthentication()?
  3. Je zaregistrovaný services.AddAuthorization() ?

Konfigurační klíč nebyl nalezen.

Chyba: Ověření oboru selže bezobslužně.

Kontrola:

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

Zkontrolujte, že cesta konfigurace přesně odpovídá.