Självstudie: Skydda ett ASP.NET webb-API som är registrerat i en kundklientorganisation
Webb-API:er kan innehålla information som kräver användarautentisering och auktorisering. Program kan använda delegerad åtkomst, agera på uppdrag av en inloggad användare eller endast appåtkomst, som endast fungerar som programmets egen identitet när du anropar skyddade webb-API:er.
I den här självstudien skapar vi ett webb-API som publicerar både delegerade behörigheter (omfång) och programbehörigheter (approller). Klientappar som webbappar som hämtar token för en inloggad användare använder de delegerade behörigheterna. Klientappar som daemonappar som hämtar token själva använder programbehörigheterna.
I den här guiden får du lära dig att:
Konfigurera din webb-API tp använda dess appregistreringsinformation Konfigurera ditt webb-API för att använda delegerade och programbehörigheter som registrerats i appregistreringen Skydda dina webb-API-slutpunkter
Förutsättningar
En API-registrering som exponerar minst ett omfång (delegerade behörigheter) och en approll (programbehörighet) som ToDoList.Read. Om du inte redan har gjort det registrerar du ett API i Microsoft Entra administrationscenter genom att följa registreringsstegen. Kontrollera att du har följande:
- Program-ID (klient) för webb-API:et
- Katalog-ID för webb-API:et (klientorganisation) är registrerat
- Underdomän för katalog (klientorganisation) där webb-API:et är registrerat. Om din primära domän till exempel är contoso.onmicrosoft.com är underdomänen Directory (klientorganisation) contoso.
- ToDoList.Read och ToDoList.ReadWrite som delegerade behörigheter (omfång) som exponeras av webb-API:et.
- ToDoList.Read.All och ToDoList.ReadWrite.All som programbehörigheter (approller) som exponeras av webb-API:et.
.NET 7.0 eller senare.
Visual Studio Code eller någon annan kodredigerare.
Skapa ett ASP.NET Core webb-API
Öppna terminalen och navigera sedan till mappen där du vill att projektet ska finnas.
Kör följande kommandon:
dotnet new webapi -o ToDoListAPI cd ToDoListAPI
När en dialogruta frågar om du vill lägga till nödvändiga tillgångar i projektet väljer du Ja.
Installera paket
Installera följande paket:
Microsoft.EntityFrameworkCore.InMemory
som gör att Entity Framework Core kan användas med en minnesintern databas. Den är inte avsedd för produktionsanvändning.Microsoft.Identity.Web
förenklar tillägg av stöd för autentisering och auktorisering för webbappar och webb-API:er som integreras med Microsofts identitetsplattform.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Konfigurera appregistreringsinformation
Öppna filen appsettings.json i appmappen och lägg till den appregistreringsinformation som du registrerade när du registrerade webb-API:et.
{
"AzureAd": {
"Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
},
"Logging": {...},
"AllowedHosts": "*"
}
Ersätt följande platshållare enligt följande:
- Ersätt
Enter_the_Application_Id_Here
med ditt program-ID (klient). - Ersätt
Enter_the_Tenant_Id_Here
med ditt katalog-ID (klientorganisation). - Ersätt
Enter_the_Tenant_Subdomain_Here
med underdomänen Katalog (klientorganisation).
Lägg till approll och omfång
Alla API:er måste publicera minst ett omfång, även kallat delegerad behörighet, för att klientapparna ska kunna hämta en åtkomsttoken för en användare. API:er bör också publicera minst en approll för program, även kallat programbehörighet, för att klientapparna ska kunna hämta en åtkomsttoken som sig själva, det vill s.v.s. när de inte loggar in en användare.
Vi anger dessa behörigheter i filen appsettings.json . I den här självstudien har vi registrerat fyra behörigheter. ToDoList.ReadWrite och ToDoList.Read som delegerade behörigheter och ToDoList.ReadWrite.All och ToDoList.Read.All som programbehörigheter.
{
"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": "*"
}
Lägg till autentiseringsschema
Ett autentiseringsschema namnges när autentiseringstjänsten konfigureras under autentiseringen. I den här artikeln använder vi JWT-ägarautentiseringsschemat. Lägg till följande kod i filen Programs.cs för att lägga till ett autentiseringsschema.
// 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);
Skapa dina modeller
Skapa en mapp med namnet Modeller i rotmappen för projektet. Navigera till mappen och skapa en fil med namnet ToDo.cs och lägg sedan till följande kod. Den här koden skapar en modell med namnet 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;
}
Lägga till en databaskontext
Databaskontexten är huvudklassen som samordnar Entity Framework-funktioner för en datamodell. Den här klassen skapas genom att härledas från klassen Microsoft.EntityFrameworkCore.DbContext . I den här självstudien använder vi en minnesintern databas i testsyfte.
Skapa en mapp med namnet DbContext i projektets rotmapp.
Navigera till mappen och skapa en fil med namnet ToDoContext.cs och lägg sedan till följande innehåll i filen:
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; } }
Öppna filen Program.cs i appens rotmapp och lägg sedan till följande kod i filen. Den här koden registrerar en
DbContext
underklass som kallasToDoContext
för en begränsad tjänst i ASP.NET Core programtjänstleverantör (kallas även för containern för beroendeinmatning). Kontexten är konfigurerad för att använda den minnesinterna databasen.// Add the following to your imports using ToDoListAPI.Context; using Microsoft.EntityFrameworkCore; builder.Services.AddDbContext<ToDoContext>(opt => opt.UseInMemoryDatabase("ToDos"));
Lägga till kontrollanter
I de flesta fall skulle en kontrollant ha mer än en åtgärd. Vanligtvis skapar, läser, uppdaterar och tar bort (CRUD) åtgärder. I den här självstudien skapar vi bara två åtgärdsobjekt. Ett läsa alla åtgärdsobjekt och ett skapa åtgärdsobjekt för att visa hur du skyddar dina slutpunkter.
Gå till mappen Controllers i rotmappen för projektet.
Skapa en fil med namnet ToDoListController.cs i den här mappen. Öppna filen och lägg sedan till följande pannplåtskod:
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(){...} }
Lägga till kod i kontrollanten
I det här avsnittet lägger vi till kod i platshållarna som vi skapade. Fokus här är inte på att skapa API:et, utan snarare på att skydda det.
Importera nödvändiga paket. Microsoft.Identity.Web-paketet är en MSAL-omslutning som hjälper oss att enkelt hantera autentiseringslogik, till exempel genom att hantera tokenvalidering. För att säkerställa att våra slutpunkter kräver auktorisering använder vi det inbyggda Microsoft.AspNetCore.Authorization-paketet .
Eftersom vi har beviljat behörigheter för att det här API:et ska anropas antingen med delegerade behörigheter för användarens eller programmets behörigheter där klienten anropar som sig själv och inte för användarens räkning, är det viktigt att veta om anropet görs av appen för egen räkning. Det enklaste sättet att göra detta är anspråken för att ta reda på om åtkomsttoken innehåller det valfria anspråket
idtyp
. Det häridtyp
anspråket är det enklaste sättet för API:et att avgöra om en token är en apptoken eller en app + användartoken. Vi rekommenderar att du aktiverar det valfria anspråketidtyp
.Om anspråket
idtyp
inte är aktiverat kan du använda anspråkenroles
ochscp
för att avgöra om åtkomsttoken är en apptoken eller en app + användartoken. En åtkomsttoken som utfärdats av Microsoft Entra External ID har minst ett av de två anspråken. Åtkomsttoken som utfärdas till en användare har anspråketscp
. Åtkomsttoken som utfärdas till ett program har anspråketroles
. Åtkomsttoken som innehåller båda anspråken utfärdas endast till användare, där anspråketscp
anger de delegerade behörigheterna, medan anspråketroles
anger användarens roll. Åtkomsttoken som inte har någondera ska respekteras.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"); } }
Lägg till en hjälpfunktion som avgör om begäran som görs innehåller tillräckligt med behörigheter för att utföra den avsedda åtgärden. Kontrollera om det är appen som gör begäran för egen räkning eller om appen anropar för en användare som äger den angivna resursen genom att verifiera användar-ID:t.
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; }
Anslut dina behörighetsdefinitioner för att skydda vägar. Skydda ditt API genom att lägga till
[Authorize]
attributet i kontrollantklassen. Detta säkerställer att kontrollantåtgärderna endast kan anropas om API:et anropas med en auktoriserad identitet. Behörighetsdefinitionerna definierar vilka typer av behörigheter som krävs för att utföra dessa åtgärder.[Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController: ControllerBase{...}
Lägg till behörigheter till GET all-slutpunkten och POST-slutpunkten. Gör detta med hjälp av metoden RequiredScopeOrAppPermission som ingår i namnområdet Microsoft.Identity.Web.Resource . Sedan skickar du omfång och behörigheter till den här metoden via attributen RequiredScopesConfigurationKey och RequiredAppPermissionsConfigurationKey .
[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); }
Kör ditt API
Kör ditt API för att säkerställa att det körs bra utan fel med kommandot dotnet run
. Om du tänker använda https-protokollet även under testningen måste du lita på . NET:s utvecklingscertifikat.
Ett fullständigt exempel på den här API-koden finns i exempelfilen.