Självstudie: Skydda ett ASP.NET Core-webb-API som registrerats i en extern klientorganisation
Den här självstudieserien visar hur du skyddar ett registrerat webb-API i den externa klientorganisationen. I den här självstudien skapar du ett ASP.NET Core-webb-API som publicerar både delegerade behörigheter (omfång) och programbehörigheter (approller).
I den här självstudien;
- Konfigurera webb-API:et så att det använder sin appregistreringsinformation
- Konfigurera ditt webb-API för att använda delegerade behörigheter 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 administrationscentret för Microsoft Entra genom att följa registreringsstegen. Kontrollera att du har följande:
- Program-ID (klient) för webb-API:et
- Katalog-ID (klientorganisation) för webb-API:et ä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 Katalog (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 SDK 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 till webbappar och webb-API:er som integreras med Microsofts identitetsplattform.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Konfigurera information om appregistrering
Öppna appsettings.json-filen i appmappen och lägg till den appregistreringsinformation som du registrerade efter registreringen av 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-ID). - Ersätt
Enter_the_Tenant_Id_Here
med ditt katalog-ID (klientorganisation). - Ersätt
Enter_the_Tenant_Subdomain_Here
med underdomänen Katalog (klientorganisation).
Använda anpassad URL-domän (valfritt)
Använd en anpassad domän för att helt märka autentiserings-URL:en. Från ett användarperspektiv finns användarna kvar på din domän under autentiseringsprocessen, i stället för att omdirigeras till ciamlogin.com domännamn.
Följ dessa steg för att använda en anpassad domän:
Använd stegen i Aktivera anpassade URL-domäner för appar i externa klienter för att aktivera anpassad URL-domän för din externa klientorganisation.
Öppna appsettings.json fil:
- Uppdatera värdet för
Instance
egenskapen till https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. ErsättEnter_the_Custom_Domain_Here
med din anpassade URL-domän ochEnter_the_Tenant_ID_Here
med ditt klient-ID. Om du inte har ditt klientorganisations-ID lär du dig att läsa klientinformationen. - Lägg till
knownAuthorities
egenskap med ett värde [Enter_the_Custom_Domain_Here].
- Uppdatera värdet för
När du har gjort ändringarna i din appsettings.json-fil , om din anpassade URL-domän är login.contoso.com och klientorganisations-ID:t är aaaabbbb-0000-cccc-1111-dddd222eeee, bör filen se ut ungefär så här:
{
"AzureAd": {
"Instance": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
"KnownAuthorities": ["login.contoso.com"]
},
"Logging": {...},
"AllowedHosts": "*"
}
Lägga 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 få 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, dvs. 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. Gå 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 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ören (även kallad containern för beroendeinmatning). Kontexten är konfigurerad för att använda minnesintern databas.// 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 som visar 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 till de platshållare 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 paketet Microsoft.AspNetCore.Authorization .
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 Externt 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ågotdera 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 gör anropet åt 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 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 api:et för att säkerställa att det fungerar 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.