Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Se aplica a: inquilinos de personal
inquilinos externos (más información)
En esta serie de tutoriales se muestra cómo proteger una API web de ASP.NET Core con la plataforma de identidad de Microsoft para limitar el acceso solo a usuarios autorizados y aplicaciones cliente. La API web que compila usa permisos delegados (ámbitos) y permisos de aplicación (roles de aplicación).
En este tutorial, harás lo siguiente:
- Compilación de una API web de ASP.NET Core
- Configuración de la API web para usar los detalles de registro de aplicaciones de Microsoft Entra
- Protección de los puntos de conexión de API web
- Ejecución de la API web para asegurarse de que escucha solicitudes HTTP
Prerrequisitos
- Si aún no lo ha hecho, complete los pasos descritos en Inicio rápido: Llamada a una API web protegida por la plataforma de identidad de Microsoft. No tiene que clonar y ejecutar el ejemplo de código, pero asegúrese de que tiene lo siguiente:
- Los detalles del registro de aplicación de la API web del Centro de administración de Microsoft Entra, incluido el id. de cliente y el id. de inquilino.
- ToDoList.Read y ToDoList.ReadWrite como permisos delegados (ámbitos) expuestos por la API web
- ToDoList.Read.All y ToDoList.ReadWrite.All como los permisos de aplicación (roles de aplicación) expuestos por la API web
- SDK de .NET 8.0 o posterior.
- Visual Studio Code u otro editor de código.
Creación de un nuevo proyecto de API web de ASP.NET Core
Para crear un proyecto de API web de ASP.NET core mínimo, siga estos pasos:
Abra el terminal en Visual Studio Code o en cualquier otro editor de código y vaya al directorio donde desea crear el proyecto.
Ejecute los comandos siguientes en la CLI de .NET o en cualquier otra herramienta de línea de comandos.
dotnet new web -o TodoListApi cd TodoListApi
Seleccione Sí cuando un cuadro de diálogo le pregunte si desea confiar en los autores.
Seleccione Sí Cuando un cuadro de diálogo le pregunte si desea agregar los recursos necesarios al proyecto.
Instalación de paquetes necesarios
Para compilar, proteger y probar la API web de ASP.NET Core, debe instalar los siguientes paquetes:
Microsoft.EntityFrameworkCore.InMemory
: un paquete que permite usar Entity Framework Core con una base de datos en memoria. Resulta útil para realizar pruebas, pero no está diseñada para su uso en producción.Microsoft.Identity.Web
: un conjunto de bibliotecas de ASP.NET Core que simplifican la adición de compatibilidad de autenticación y autorización a aplicaciones web y API web que se integran con la plataforma de identidad de Microsoft.
Para instalar el paquete, use:
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Configuración de los detalles de registro de una aplicación
Abra el archivo appsettings.json en la carpeta de la aplicación y agregue los detalles de registro de la aplicación que registró después de registrar la API web.
{
"AzureAd": {
"Instance": "Enter_the_Authority_URL_Here",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here"
},
"Logging": {...},
"AllowedHosts": "*"
}
Reemplace los siguientes marcadores de posición como se indica:
- Reemplace
Enter_the_Application_Id_Here
por id. de la aplicación (cliente). - Reemplace
Enter_the_Tenant_Id_Here
con su ID de Directorio (tenant). - Reemplace por
Enter_the_Authority_URL_Here
la dirección URL de la entidad, como se explica en la sección siguiente.
URL de autoridad de tu aplicación
La dirección URL de autoridad especifica el directorio desde el que la Biblioteca de autenticación de Microsoft (MSAL) puede solicitar tokens. Puede compilarlo de forma diferente tanto en los inquilinos de recursos como en los externos, como se muestra a continuación:
//Instance for workforce tenant
Instance: "https://login.microsoftonline.com/"
Uso del dominio de dirección URL personalizado (opcional)
Los dominios de dirección URL personalizados no se admiten en los inquilinos de recursos de trabajo.
Agregar permisos
Todas las API deben publicar al menos un ámbito, también denominado permiso delegado, para que las aplicaciones cliente obtengan un token de acceso para un usuario. Las APIs también deben publicar un mínimo de un rol de aplicación, también denominado permisos de aplicación, para que las aplicaciones cliente obtengan un token de acceso por sí mismas, es decir, cuando no están iniciando sesión de un usuario.
Especificamos estos permisos en el archivo appsettings.json. En este tutorial, registró los siguientes permisos delegados y de aplicación:
- Permisos delegados:ToDoList.Read y ToDoList.ReadWrite.
- Permisos de aplicaciones:ToDoList.Read.All y ToDoList.ReadWrite.All.
Cuando una aplicación cliente o usuario llama a la API web, solo los clientes con estos ámbitos o permisos obtienen autorización para acceder al punto de conexión protegido.
{
"AzureAd": {
"Instance": "Enter_the_Authority_URL_Here",
"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": "*"
}
Implementación de la autenticación y autorización en la API
Para configurar la autenticación y la autorización, abra el program.cs
archivo y reemplace su contenido por los siguientes fragmentos de código:
Adición de un esquema de autenticación
En esta API, usamos el esquema de Bearer JSON Web Token (JWT) como mecanismo de autenticación predeterminado. Use el AddAuthentication
método para registrar el esquema de portador JWT.
// Add required packages to your imports
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add an authentication scheme
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration);
Creación del modelo de la aplicación
En la carpeta raíz del proyecto, cree una carpeta denominada Models. Vaya a la carpeta Models y cree un archivo denominado ToDo.cs
y agregue el código siguiente.
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;
}
El código anterior crea un modelo denominado ToDo. Este modelo representa los datos que administra la aplicación.
Incorporación de un contexto de base de datos
A continuación, definimos una clase de contexto de base de datos, que coordina la funcionalidad de Entity Framework para un modelo de datos. Esta clase hereda de la clase Microsoft.EntityFrameworkCore.DbContext que administra las interacciones entre la aplicación y la base de datos. Para agregar el contexto de la base de datos, siga estos pasos:
Cree una carpeta denominada DbContext en la carpeta raíz del proyecto.
Vaya a la carpeta DbContext y cree un archivo denominado
ToDoContext.cs
y agregue el código siguiente: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; } }
Abra el archivo Program.cs en la carpeta raíz del proyecto y actualícelo con el código siguiente:
// Add the following to your imports using ToDoListAPI.Context; using Microsoft.EntityFrameworkCore; //Register ToDoContext as a service in the application builder.Services.AddDbContext<ToDoContext>(opt => opt.UseInMemoryDatabase("ToDos"));
En el fragmento de código anterior, registramos el contexto de base de datos como un servicio con ámbito en el proveedor de servicios de aplicaciones de ASP.NET Core (también conocido como contenedor de inserción de dependencias). También configurará la clase ToDoContext
para usar una base de datos en memoria para la API de lista de todo.
Configuración de un controlador
Los controladores suelen implementar acciones Crear, Leer, Actualizar y Eliminar (CRUD) para administrar recursos. Dado que este tutorial se centra más en la protección de los puntos de conexión de API, solo se implementan dos elementos de acción en el controlador. Acción Leer todo para recuperar todos los elementos To-Do y una acción Crear para agregar un nuevo elemento To-Do. Siga estos pasos para agregar un controlador al proyecto:
Vaya a la carpeta raíz del proyecto y cree una carpeta denominada Controllers.
Crea un archivo denominado
ToDoListController.cs
dentro de la carpeta Controllers y agrega el siguiente código predefinido:
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(){...}
}
Agregar código al controlador
Esta sección explica cómo agregar código al controlador estructurado en la sección anterior. El enfoque aquí es proteger la API, no desarrollarla.
Importe los paquetes necesarios: El
Microsoft.Identity.Web
paquete es un contenedor alrededor de MSAL.NET que nos ayuda a controlar fácilmente la lógica de autenticación, como controlar la validación de tokens. Para asegurarse de que nuestros puntos de conexión requieren autorización, usamos el paquete integradoMicrosoft.AspNetCore.Authorization
.Dado que se han concedido permisos para llamar a esta API mediante permisos delegados en nombre del usuario o permisos de aplicación en los que el cliente llama como sí mismo y no en nombre del usuario, es importante saber si la aplicación realiza la llamada en su propio nombre. La manera más fácil de hacerlo es encontrar si el token de acceso contiene la notificación opcional
idtyp
. Esta notificaciónidtyp
es la forma más sencilla para que una API determine si un token es un token de aplicación o un token de usuario + aplicación. Recomendamos habilitar la reclamación opcionalidtyp
.Si la notificación
idtyp
no está habilitada, puede usar las notificacionesroles
yscp
para determinar si el token de acceso es un token de aplicación o un token de usuario + aplicación. Un token de acceso emitido por Microsoft Entra ID tiene al menos uno de los dos atributos. Los tokens de acceso emitidos para un usuario tienen la notificaciónscp
. Los tokens de acceso emitidos para una aplicación tienen la notificaciónroles
. Los tokens de acceso que contienen ambas notificaciones solo se emiten a los usuarios, donde la notificaciónscp
designa los permisos delegados, mientras que la notificaciónroles
designa el rol del usuario. No se respetan los tokens de acceso que no tienen ninguna.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"); } }
Agregue una función auxiliar que determine si la solicitud que se realiza contiene suficientes permisos para llevar a cabo la acción prevista. Compruebe si es la aplicación la que realiza la solicitud en su propio nombre o si la aplicación está realizando la llamada en nombre de un usuario propietario del recurso en cuestión mediante la validación del identificador de usuario.
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; }
Conecte las definiciones de permisos para proteger las rutas. Proteja la API agregando el atributo
[Authorize]
a la clase de controlador. Se garantiza así que solo se pueda llamar a las acciones de controlador si se llama a la API con una identidad autorizada. Las definiciones de permisos definen qué tipos de permisos son necesarios para realizar estas acciones.[Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController: ControllerBase{...}
Agregue permisos a los puntos de conexión GET y POST. Para ello, use el método RequiredScopeOrAppPermission que forma parte del espacio de nombres Microsoft.Identity.Web.Resource. A continuación, pase los ámbitos y permisos a este método a través de los atributos RequiredScopesConfigurationKey y 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); }
Configuración del middleware de API para usar el controlador
A continuación, configuramos la aplicación para que reconozca y use controladores para controlar las solicitudes HTTP. Abra el archivo y agregue el program.cs
código siguiente para registrar los servicios de controlador en el contenedor de inserción de dependencias.
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
En el fragmento de código anterior, el AddControllers()
método prepara la aplicación para usar controladores mediante el registro de los servicios necesarios mientras asigna MapControllers()
las rutas del controlador para controlar las solicitudes HTTP entrantes.
Ejecutar la API
Ejecute la API para asegurarse de que se está ejecutando sin errores mediante el comando dotnet run
. Si piensa usar el protocolo HTTPS incluso durante las pruebas, debe confiar en el certificado de desarrollo de .NET.
Inicie la aplicación escribiendo lo siguiente en el terminal:
dotnet run
Se debe mostrar una salida similar a la siguiente en el terminal, lo que confirma que la aplicación se está ejecutando en
http://localhost:{port}
y escuchando peticiones.Building... info: Microsoft.Hosting.Lifetime[0] Now listening on: http://localhost:{port} info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. ...
La página web http://localhost:{host}
muestra una salida similar a la siguiente imagen. Esto se debe a que se llama a la API sin autenticación. Para realizar una llamada autorizada, consulte Pasos siguientes para obtener instrucciones sobre cómo acceder a una API web protegida.
Para obtener un ejemplo completo de este código de API, consulte el archivo de ejemplos.