Implementera auktorisering i webb-API:er med Microsoft. Identity.Web

I den här artikeln implementerar du auktorisering i ASP.NET Core webb-API:er med hjälp av Microsoft. Identity.Web. Du validerar omfång (delegerade behörigheter) och appbehörigheter (programbehörigheter) för att kontrollera åtkomsten till skyddade resurser. I exemplen används Microsoft Entra ID som identitetsprovider.

Förstå auktoriseringsbegrepp

Det här avsnittet beskriver de viktigaste skillnaderna mellan autentisering och auktorisering och beskriver vad Microsoft. Identity.Web verifierar i åtkomsttoken.

Autentisering kontra auktorisering

Begrepp Avsikt Result
autentisering Verifiera identitet 401 Obehörig om det misslyckas
Behörighet Kontrollera behörigheter 403 Förbjudet om otillräckligt

Vad valideras

När ett webb-API tar emot en åtkomsttoken Microsoft. Identity.Web verifierar:

  1. Tokensignatur – kommer den från en betrodd utfärdare?
  2. Token-målgrupp – är den avsedd för det här API:et?
  3. Förfallodatum för token – är den fortfarande giltig?
  4. Omfång/roller – Har klientappen och användaren rätt behörigheter?

Den här guiden fokuserar på #4 – validering av omfång och appbehörigheter.

Omfång (delegerade behörigheter)

Omfång gäller när en användare delegerar behörighet till en app att agera för deras räkning (till exempel ett webb-API som anropas för en inloggad användares räkning).

Detaljer Värde
Tokenpåstående scp eller scope (klientapp); roles (användare)
Exempelvärden "access_as_user", "User.Read", "Files.ReadWrite"

Appbehörigheter (programbehörigheter)

Appbehörigheter gäller när en app anropar webb-API:et som sig själv utan användarkontext, till exempel en daemon- eller bakgrundstjänst med klientautentiseringsuppgifter.

Detaljer Värde
Tokenpåstående roles
Exempelvärden "Mail.Read.All", "User.Read.All"

Verifiera omfång med RequiredScope

Attributet RequiredScope kontrollerar att åtkomsttoken innehåller minst ett av de angivna omfången. Använd det här attributet när ditt API endast hanterar användar delegerade begäranden.

Konfigurera omfångsverifiering

Följ de här stegen för att aktivera omfångsverifiering i ditt API.

1. Aktivera auktorisering i ditt API:

Lägg till autentiserings- och auktoriseringstjänster i din programpipeline:

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. Skydda styrenheter eller åtgärder:

Tillämpa attributen [Authorize] och [RequiredScope] på din kontrollant eller enskilda åtgärder:

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

Tillämpa omfångsmönster

Välj det mönster som passar bäst för hur du hanterar omfång i ditt program.

Mönster 1: Hårdkodade skop

Använd det här mönstret när omfången är fasta och kända vid utvecklingstillfället.

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

Om du vill acceptera något av flera omfång anger du dem som parametrar:

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

Mönster 2: Räckvidd från konfiguration

Använd det här mönstret när omfång ska vara konfigurerbara per miljö. Definiera omfången i konfigurationsfilen:

appsettings.json:

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

Referera till konfigurationsnyckeln i kontrollanten:

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

Med den här metoden kan du ändra omfång utan att kompilera om.

Mönster 3: Omfång på åtgärdsnivå

Använd det här mönstret när olika åtgärder kräver olika behörigheter. Tillämpa [RequiredScope] på individuella metodaktioner:

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

Förstå valideringsflödet

När en begäran tas emot bearbetar mellanprogrammet den i följande ordning:

  1. ASP.NET Core mellanprogram för autentisering validerar token
  2. RequiredScope kontrollerar attributet för kravet scp eller scope
  3. Om en token innehåller minst ett matchande omfång fortsätter begäran.
  4. Om inget matchande omfång hittas returnerar API:et ett 403-förbjudet svar.

I följande exempel visas ett typiskt felsvar:

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

Verifiera appbehörigheter med RequiredScopeOrAppPermission

Attributet RequiredScopeOrAppPermission verifierar antingen omfång (delegerade) eller appbehörigheter (program). Använd det här attributet när ditt API hanterar både användardelegeringsappar och daemon-/tjänstappar från samma slutpunkt.

Om ditt API endast hanterar användar delegerade begäranden använder du RequiredScope i stället.

Konfigurera omfångs- eller appbehörighetsverifiering

Använd attributet för att acceptera någon av tokentyperna:

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

Konfigurera appbehörigheter från inställningar

Lagra omfång och appbehörigheter i konfigurationen för att ändra dem utan att kompilera om.

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

Referera till konfigurationsnycklarna i kontrollanten:

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

Jämföra skillnader i tokenkrav

Följande tabell visar hur anspråk skiljer sig mellan användardelegering och endast apptoken:

Tokentyp Anspråk Exempelvärde
Användardelegering scp eller scope "access_as_user User.Read"
Endast app roles ["TodoList.ReadWrite.All"]

I följande exempel visas en användardelegeringstoken:

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

I följande exempel visas ett app-endast-token:

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

Skapa auktoriseringsprinciper

För komplexa auktoriseringsscenarier använder du ASP.NET Core auktoriseringsprinciper. Med principer kan du centralisera regler, kombinera flera krav och skriva testbar auktoriseringslogik.

Förmån Beskrivning
Centraliserad logik Definiera auktoriseringsregler en gång, återanvänd överallt
Komponerbar Kombinera flera krav (omfång + anspråk + anpassad logik)
Testbar Det är enklare att enhetstesta auktoriseringslogik
Böjlig Anpassade krav utöver omfångsverifiering

Mönster 1: Definiera en policy med RequireScope

Definiera namngivna principer som kräver specifika omfång och referera sedan till dem på dina 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();

Tillämpa principerna på kontrollantåtgärder:

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

Mönster 2: Definiera en policy med ScopeAuthorizationRequirement

Använd ScopeAuthorizationRequirement för mer explicita omfångskrav:

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

Mönster 3: Ange en standardpolicy

Ange en standardprincip som gäller för alla [Authorize] attribut automatiskt:

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

    options.DefaultPolicy = defaultPolicy;
});

Varje [Authorize] attribut kräver nu omfånget access_as_user :

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

Mönster 4: Kombinera flera krav

Kombinera omfångs-, roll- och autentiseringskrav i en enda princip:

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

Mönster 5: Skapa en princip från konfigurationen

Läs in omfång från konfigurationen för att hålla principer miljöspecifika:

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

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

Filtrera begäranden efter klientorganisation

Begränsa API-åtkomst till token från specifika Microsoft Entra klienter. Detta är användbart när ditt API för flera klientorganisationer endast ska acceptera begäranden från godkända kundklientorganisationer.

Begränsa åtkomsten till tillåtna klienter

Definiera en princip som kontrollerar hyresgäst-ID-kravet mot en tillåten lista.

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

Konfigurera klientfiltrering från inställningar

Lagra tillåtna hyresgäst-ID:n i konfigurationen för att hantera dem utan kodändringar.

appsettings.json:

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

Läs klientlistan och skapa regeln vid uppstart.

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

Kombinera omfång med hyresgästfiltrering

Skapa en princip som kräver både ett giltigt omfång och en godkänd klientorganisation:

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

Följ metodtipsen

Använd dessa rekommendationer för att skapa säker, underhållsbar auktoriseringslogik.

Saker att göra

1. Kombinera [Authorize] alltid med omfångsvalidering:

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

2. Använd konfiguration för miljöspecifika omfång:

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

3. Använd minst behörighet:

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

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

4. Använd principer för komplex auktorisering:

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

5. Aktivera detaljerade felsvar under utveckling:

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

Förbud

1. Hoppa inte över [Authorize] när du använder 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. Hårdkoda inte klient-ID:t i 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. Blanda inte ihop behörigheter med roller:

//  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. Exponera inte känslig omfångsinformation i produktionsfelmeddelanden:

Konfigurera lämpliga loggningsnivåer och felhantering för produktionsmiljöer.


Felsöka auktoriseringsproblem

Använd följande vägledning för att diagnostisera vanliga auktoriseringsproblem.

403 Förbjudet – omfång saknas

Fel: API returnerar 403 även med en giltig token.

Diagnos:

  1. Avkoda token på jwt.ms.
  2. Kontrollera anspråket scp eller scope .
  3. Kontrollera att värdet matchar attributet RequiredScope .

Lösning:

  • Se till att klientappen begär rätt omfång när en token erhålls.
  • Kontrollera att omfånget exponeras i API-appregistreringen i Microsoft Entra.
  • Bevilja administratörsmedgivande om det behövs.

RequiredScope fungerar inte

Symptom: Attributet verkar ignoreras.

Kontrollera följande:

  1. Har du lagt till attributet [Authorize] ?
  2. Anropas app.UseAuthorization() efter app.UseAuthentication()?
  3. Är services.AddAuthorization() registrerad?

Konfigurationsnyckeln hittades inte

Fel: Omfångsvalidering misslyckas tyst.

Kontrollera följande:

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

Kontrollera att konfigurationssökvägen matchar exakt.