Implementeer autorisatie in web-API's met Microsoft. Identity.Web

In dit artikel implementeert u autorisatie in ASP.NET Core web-API's met behulp van Microsoft. Identity.Web. U valideert bereiken (gedelegeerde machtigingen) en app-machtigingen (toepassingsmachtigingen) om de toegang tot beveiligde resources te beheren. In de voorbeelden wordt Microsoft Entra ID gebruikt als id-provider.

Inzicht in autorisatieconcepten

In deze sectie worden de belangrijkste verschillen tussen verificatie en autorisatie beschreven en wordt beschreven wat Microsoft. Identity.Web valideert in toegangstokens.

Verificatie versus autorisatie

Concept Purpose Resultaat
Authentication Identiteit verifiëren 401 Niet geautoriseerd als mislukt
Authorization Machtigingen controleren 403 Verboden indien onvoldoende

Wat wordt gevalideerd

Wanneer een web-API een toegangstoken ontvangt, Microsoft. Identity.Web valideert:

  1. Tokenhandtekening - Is het afkomstig van een vertrouwde instantie?
  2. Token gebruikers - is het bedoeld voor deze API?
  3. Verloop van token - Is het nog steeds geldig?
  4. Scopes/rollen - Heeft de client-app of het onderwerp (gebruiker) de juiste machtigingen?

Deze handleiding is gericht op #4: toepassingsbereiken en app-machtigingen valideren.

Bereiken (gedelegeerde machtigingen)

Toepassingen zijn van toepassing wanneer een gebruiker machtigingen voor een app delegeert om namens hem of haar te handelen (bijvoorbeeld een web-API die namens een aangemelde gebruiker wordt aangeroepen).

Het detail Waarde
Tokenclaim scp of scope (client-app); roles (gebruiker)
Voorbeeldwaarden "access_as_user", "User.Read", "Files.ReadWrite"

App-machtigingen (toepassingsmachtigingen)

App-machtigingen zijn van toepassing wanneer een app de web-API aanroept als zichzelf zonder gebruikerscontext, zoals een daemon of achtergrondservice met behulp van clientreferenties.

Het detail Waarde
Tokenclaim roles
Voorbeeldwaarden "Mail.Read.All", "User.Read.All"

Scopes valideren met RequiredScope

Het RequiredScope kenmerk controleert of het toegangstoken ten minste één van de gespecificeerde scopes bevat. Gebruik dit kenmerk wanneer uw API alleen door de gebruiker gedelegeerde aanvragen verwerkt.

Scopevalidatie instellen

Volg deze stappen om bereikvalidatie in te schakelen in uw API.

1. Autorisatie inschakelen in uw API:

Verificatie- en autorisatieservices toevoegen aan uw toepassingspijplijn:

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. Controllers of acties beveiligen:

Pas de [Authorize] en [RequiredScope] kenmerken toe op uw controller of afzonderlijke acties:

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

Scopepatronen implementeren

Kies het patroon dat het beste past bij hoe u schalen beheert in uw toepassing.

Patroon 1: vastgelegde bereiken

Gebruik dit patroon wanneer de scopes vast en bekend zijn tijdens de ontwikkeling.

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

Als u een van meerdere scopes wilt accepteren, vermeld ze dan als parameters:

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

Scopen vanuit configuratie

Gebruik dit patroon wanneer scopes per omgeving configureerbaar moeten zijn. Definieer de scopen in uw configuratiebestand.

appsettings.json:

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

Verwijs naar de configuratiesleutel in uw controller:

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

Met deze methode kunt u de scopes wijzigen zonder opnieuw te moeten compileren.

Patroon 3: Toepassingsgebied op actieniveau

Gebruik dit patroon wanneer voor verschillende acties verschillende machtigingen zijn vereist. Toepassen [RequiredScope] op afzonderlijke actiemethoden:

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

Begrijp de validatiestroom

Wanneer een aanvraag binnenkomt, verwerkt de middleware deze in de volgende volgorde:

  1. ASP.NET Core verificatie-middleware valideert het token
  2. RequiredScope kenmerk controleert op de scp of scope claim
  3. Als het token ten minste één overeenkomend bereik bevat, wordt de aanvraag voortgezet.
  4. Als er geen overeenkomend bereik wordt gevonden, retourneert de API een 403 Verboden antwoord.

In het volgende voorbeeld ziet u een typische foutreactie:

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

App-machtigingen valideren met RequiredScopeOrAppPermission

Het RequiredScopeOrAppPermission kenmerk valideert bereiken (gedelegeerd) of app-machtigingen (toepassing). Gebruik dit kenmerk wanneer uw API zowel door de gebruiker gedelegeerde apps als daemon-/service-apps van hetzelfde eindpunt bedient.

Als uw API alleen door de gebruiker gedelegeerde aanvragen verwerkt, moet u in plaats daarvan RequiredScope gebruiken.

Validatie van bereik- of app-machtigingen instellen

Pas het kenmerk toe om een van de tokentypen te accepteren:

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

App-machtigingen configureren vanuit instellingen

Sla scopes en app-machtigingen op binnen de configuratie om ze te wijzigen zonder hercompilatie.

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

Raadpleeg de configuratiesleutels in uw controller:

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

Verschillen tussen tokenclaims vergelijken

In de volgende tabel ziet u hoe claims verschillen tussen door de gebruiker gedelegeerde tokens en alleen-app-tokens.

Token type Aanspraak Voorbeeldwaarde
Door de gebruiker gedelegeerd scp of scope "access_as_user User.Read"
Alleen voor app roles ["TodoList.ReadWrite.All"]

In het volgende voorbeeld ziet u een door de gebruiker gedelegeerd token:

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

In het volgende voorbeeld ziet u een token met alleen apps:

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

Autorisatiebeleid maken

Gebruik voor complexe autorisatiescenario's ASP.NET Core autorisatiebeleid. Met beleidsregels kunt u regels centraliseren, meerdere vereisten combineren en testbare autorisatielogica schrijven.

Benefit Description
Gecentraliseerde logica Autorisatieregels eenmaal definiëren, overal opnieuw gebruiken
Samenstelbaar Meerdere vereisten combineren (scopes + claims + aangepaste logica)
Testbaar Eenvoudiger te testen autorisatielogica per eenheid
Buigzaam Aangepaste vereisten buiten de scope-validatie

Patroon 1: Een beleid definiëren met RequireScope

Definieer genaamde beleidsregels waarvoor specifieke toepassingsgebieden zijn vereist en raadpleeg deze vervolgens op uw controllers.

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

Pas het beleid toe op controlleracties:

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

Patroon 2: Een beleid definiëren met ScopeAuthorizationRequirement

Gebruik ScopeAuthorizationRequirement voor meer expliciete scope-eisen:

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

Patroon 3: Een standaardbeleid instellen

Stel automatisch een standaardbeleid in dat van toepassing is op alle [Authorize] kenmerken:

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

    options.DefaultPolicy = defaultPolicy;
});

Voor elk [Authorize] kenmerk is nu het access_as_user bereik vereist:

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

Patroon 4: Meerdere vereisten combineren

Combineer de reikwijdte-, rol- en verificatievereisten in één beleid:

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

Patroon 5: Een beleid bouwen op basis van configuratie

Scopes laden vanuit configuratie om beleidsregels omgevingsspecifiek te houden.

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

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

Aanvragen filteren op tenant

Beperk API-toegang tot tokens van specifieke Microsoft Entra tenants. Dit is handig wanneer uw API met meerdere tenants alleen aanvragen van goedgekeurde klanttenants mag accepteren.

Toegang tot toegestane tenants beperken

Definieer een beleid waarmee de tenant-id-claim wordt gecontroleerd op basis van een acceptatielijst:

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

Tenantfiltering configureren via instellingen

Sla toegestane tenant-id's op in de configuratie om ze te beheren zonder codewijzigingen.

appsettings.json:

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

Lees de tenantlijst en maak het beleid bij het opstarten:

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

Bereiken combineren met tenantfilters

Maak een beleid waarvoor zowel een geldig bereik als een goedgekeurde tenant is vereist:

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

Best practices volgen

Pas deze aanbevelingen toe om veilige, onderhoudbare autorisatielogica te bouwen.

Aanbevelingen

1. Koppel altijd [Authorize] met validatie van de scope:

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

2. Gebruik configuratie voor omgevingsspecifieke instellingen:

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

3. Minimale bevoegdheden toepassen:

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

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

4. Gebruik beleidsregels voor complexe autorisatie:

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

5. Gedetailleerde foutreacties inschakelen tijdens ontwikkeling:

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

Dingen die je niet moet doen

1. Sla [Authorize] niet over wanneer u RequiredScope gebruikt:

//  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. Codeer geen tenant-id's in productie:

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

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

3. Verwar toepassingen niet met 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. Geef geen gevoelige bereikinformatie weer in productiefoutberichten:

Configureer de juiste logboekregistratieniveaus en foutafhandeling voor productieomgevingen.


Autorisatieproblemen oplossen

Gebruik de volgende richtlijnen om veelvoorkomende autorisatieproblemen vast te stellen.

403 Verboden - ontbrekend bereik

Fout: API retourneert 403, zelfs met een geldig token.

Diagnose:

  1. Decodeer het token op jwt.ms.
  2. Controleer de scp of scope aanspraak.
  3. Controleer of de waarde overeenkomt met uw RequiredScope kenmerk.

Solution:

  • Zorg ervoor dat de client-app het juiste bereik aanvraagt bij het verkrijgen van het token.
  • Controleer of het bereik beschikbaar is in de registratie van de API-app in Microsoft Entra.
  • Ververleent beheerders toestemming indien nodig.

RequiredScope werkt niet

Symptoom: Het kenmerk lijkt te worden genegeerd.

Controleer:

  1. Hebt u het [Authorize] kenmerk toegevoegd?
  2. Wordt app.UseAuthorization() erna app.UseAuthentication()gebeld?
  3. Is services.AddAuthorization() geregistreerd?

Configuratiesleutel niet gevonden

Fout: De scopevalidatie faalt geruisloos.

Controleer:

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

Zorg ervoor dat het configuratiepad exact overeenkomt.