Leer en inglés

Compartir a través de


Configuración de la autenticación de portador de JWT en ASP.NET Core

De Damien Bowden

La autenticación de portador de JWT (JSON Web Token) se usa normalmente para las API. Aunque funciona de forma similar a la autenticación cookie, el proveedor de identidades emite un JWT o fichas tras hacer una autenticación satisfactoria. Estos tokens se pueden enviar a otros servidores para la autenticación, a diferencia de las cookies, que solo se envían al dominio emisor. Un JWT es un token autocontenido que encapsula información para un recurso de API o un cliente. El cliente que solicitó el JWT puede solicitar datos de un recurso de API mediante el encabezado Authorization y un token de portador.

La autenticación de portador de JWT proporciona:

  • Autenticación: cuando se usa el JwtBearerHandler, los tokens de portador son esenciales para la autenticación. El JwtBearerHandler valida el token y extrae la identidad del usuario de sus notificaciones.
  • Autorización: los tokens de portador habilitan la autorización proporcionando una colección de notificaciones que representan los permisos del usuario o de la aplicación, como una cookie.
  • Autorización delegada: cuando se utiliza un token de acceso específico del usuario para autenticarse entre las distintas API en lugar de un token de acceso para toda la aplicación, este proceso se conoce como autorización delegada.

Para consultar una introducción a la autenticación de portador de JWT, consulte JSON Web Tokens.Ver o descargar código de ejemplo

En este artículo se detallan las siguientes áreas:

  • Tipos de token
  • Uso de tokens de JWT para proteger API
  • ¿Cómo encaja OIDC/OAuth en todo esto?
  • Implementación de la autenticación con token portador JWT
  • Enfoques recomendados para crear un JWT

Tipos de token

Hay numerosos tipos de tokens y formatos. No se recomienda generar sus propios tokens de acceso o tokens de identificador, excepto con fines de prueba. Tokens de creación propia que no cumplen los estándares establecidos:

  • Pueden provocar vulnerabilidades de seguridad.
  • Solo son adecuados para sistemas cerrados.

Se recomienda usar OpenID Connect 1.0 o un estándar de OAuth para crear tokens de acceso diseñados para el acceso a la API.

Tokens de acceso

Tokens de acceso:

  • Son cadenas que usa una aplicación cliente para realizar solicitudes al servidor que implementa una API.
  • Pueden variar en formato. Las distintas API pueden usar formatos diferentes para los tokens.
  • Se pueden cifrar.
  • Nunca deben leerse ni interpretarse por un cliente web o una aplicación de interfaz de usuario que contenga el token de acceso.
  • Están diseñados únicamente para realizar solicitudes a una API.
  • Normalmente se envían a la API en el encabezado de solicitud de autorización como token de portador.

Consulte el Marco de autorización de OAuth 2.0

Tokens de acceso a la aplicación y tokens de acceso delegado

Los tokens de acceso pueden ser tokens de acceso a la aplicación o tokens de acceso delegados. Los tokens tienen reclamaciones diferentes y se administran y almacenan de manera diferente. Un token de acceso a la aplicación normalmente se almacena una vez en la aplicación hasta que expira, mientras que un token de acceso delegado lo almacena el usuario, ya sea en una cookie o en una caché de servidor seguro.

Se recomienda usar tokens de acceso de usuario delegado cada vez que un usuario esté implicado. Las API de bajada pueden solicitar un token de acceso de usuario delegado en nombre del usuario autenticado.

Tokens de acceso restringidos del emisor

Los tokens de acceso se pueden usar como tokens de portador o tokens con restricción de emisor para acceder a los recursos. Los tokens restringidos por el emisor requieren que el cliente solicitante demuestre que posee una clave privada para usar el token. Demostrar la posesión de una clave privada garantiza que el token no se pueda usar de forma independiente. Los tokens restringidos por el emisor se pueden implementar de dos maneras:

Tokens de identificación

Los tokens de identificador son tokens de seguridad que confirman la autenticación correcta de un usuario. Los tokens permiten al cliente comprobar la identidad del usuario. El servidor de tokens de JWT emite tokens de identificador que contienen notificaciones con información del usuario. Los tokens de ID siempre están en el formato JWT.

Los tokens de identificación no se deben nunca usar para acceder a las APIs.

Otros tokens

Hay muchos tipos de tokens, incluidos los tokens de acceso e identificador, tal y como se especifican en los estándares de OpenID Connect y OAuth. Los tokens de actualización se pueden usar para actualizar una aplicación de interfaz de usuario sin volver a autenticar al usuario. Las tokens de JAR OAuth pueden enviar de forma segura solicitudes de autorización. Los flujos de credenciales verificables usan tipos de JWT para emitir o comprobar credenciales. Es fundamental usar tokens según las especificaciones. Consulte los vínculos de estándares que se proporcionan más adelante en este artículo para obtener más información.

Uso de tokens de JWT para proteger una API

Al usar tokens de acceso de JWT para la autorización de API, la API concede o deniega el acceso en función del token proporcionado. Si la solicitud no está autorizada, se devuelve una respuesta 401 o 403. La API no debería redirigir al usuario al proveedor de identidad para obtener un nuevo token o solicitar permisos adicionales. La aplicación que consume la API es responsable de adquirir un token adecuado. Esto garantiza una separación clara de las cuestiones entre la API (autorización) y la aplicación cliente de consumo (autenticación).

Nota

HTTP también permite devolver un 404 para no autorizado, para no divulgar información sobre la existencia de recursos a clientes no autorizados.

401 No autorizado

Una respuesta 401 No autorizado indica que el token de acceso proporcionado no cumple los estándares necesarios. Esto puede deberse a varias razones, entre las que se incluyen:

  • Firma no válida: la firma del token no coincide, lo que sugiere posibles alteraciones.
  • Expiración: el token ha expirado y ya no es válido.
  • Afirmaciones incorrectas: las afirmaciones críticas dentro del token, como la audiencia (aud) o el emisor (iss), faltan o no son válidas.

Nota

De la Semántica HTTP RFC 9110: El servidor que genera una respuesta 401 DEBE enviar un campo de cabecera WWW-Authenticate (Sección 11.6.1) que contenga al menos un desafío aplicable al recurso de destino.

Las especificaciones OAuth ofrecen directrices detalladas sobre las declaraciones necesarias y su validación.

403 Prohibida

Una respuesta 403 Prohibido suele indicar que el usuario autenticado carece de los permisos necesarios para acceder al recurso solicitado. Esto es distinto de los problemas de autenticación, por ejemplo, un token no válido, y no está relacionado con las declaraciones estándar dentro del token de acceso.

En ASP.NET Core, puede aplicar la autorización mediante:

Requisitos y directivas: defina requisitos personalizados, por ejemplo, "Debe ser administrador" y asócielos a las directivas. Autorización basada en roles: asigne usuarios a roles, por ejemplo, "Administrador", "Editor" y restrinja el acceso en función de esos roles.

¿Qué rol tiene OIDC o OAuth al usar tokens de portador?

Cuando una API usa tokens de acceso de JWT para la autorización, la API solo valida el token de acceso, no cómo se obtuvo el token.

OpenID Connect (OIDC) y OAuth 2.0 proporcionan marcos estandarizados y seguros para la adquisición de tokens. La adquisición de tokens varía en función del tipo de aplicación. Debido a la complejidad de la adquisición de tokens seguros, se recomienda confiar en estos estándares:

  • Para las aplicaciones que actúan en nombre de un usuario y una aplicación: OIDC es la opción preferida, lo que permite el acceso de usuario delegado. En las aplicaciones web, se recomienda el flujo de código confidencial con clave de prueba para intercambio de códigos (PKCE) para mejorar la seguridad.
  • Si la aplicación no tiene ningún usuario: el flujo de credenciales de cliente de OAuth 2.0 es adecuado para obtener tokens de acceso a la aplicación.

Implementación de la autenticación con token portador JWT

El paquete Nuget Microsoft.AspNetCore.Authentication.JwtBearer se puede usar para validar los tokens de portador de JWT.

Los tokens de portador de JWT deben validarse completamente en una API. Debe validarse lo siguiente:

  • Firma, para confianza e integridad. Esto garantiza que el token lo creó el servicio de token seguro designado y que no ha sido manipulado.
  • Reclamación del emisor con el valor esperado.
  • Notificación de público con el valor esperado.
  • Expiración del token.

Las siguientes declaraciones son necesarias para los tokens de acceso de OAuth 2.0: iss, exp, aud, sub, client_id, iaty jti.

Si alguna de estas notificaciones o valores es incorrecto, la API debe devolver una respuesta 401.

Validación básica del token de portador de JWT

Una implementación básica de AddJwtBearer puede validar únicamente el público y el emisor. La firma debe validarse para que se pueda confiar en el token y asegurar que no haya sido alterado.

C#
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtOptions =>
{
	jwtOptions.Authority = "https://{--your-authority--}";
	jwtOptions.Audience = "https://{--your-audience--}";
});

Validación explícita del token de portador de JWT

El método AddJwtBearer proporciona varias configuraciones. Algunos proveedores de tokens seguros usan una dirección de metadatos no estándar y el parámetro se puede configurar explícitamente. La API puede aceptar varios emisores o públicos.

No es necesario definir explícitamente los parámetros. Las definiciones dependen de los valores de notificación del token de acceso y del servidor de tokens seguros que se usan para validar el token de acceso. Si es posible, debe usar los valores predeterminados.

Consulte los detalles de MapInboundClaims en Asignación de notificaciones.

C#
builder.Services.AddAuthentication()
.AddJwtBearer("some-scheme", jwtOptions =>
{
	jwtOptions.MetadataAddress = builder.Configuration["Api:MetadataAddress"];
	// Optional if the MetadataAddress is specified
	jwtOptions.Authority = builder.Configuration["Api:Authority"];
	jwtOptions.Audience = builder.Configuration["Api:Audience"];
	jwtOptions.TokenValidationParameters = new TokenValidationParameters
	{
		ValidateIssuer = true,
		ValidateAudience = true,
		ValidateIssuerSigningKey = true,
		ValidAudiences = builder.Configuration.GetSection("Api:ValidAudiences").Get<string[]>(),
		ValidIssuers = builder.Configuration.GetSection("Api:ValidIssuers").Get<string[]>()
	};

	jwtOptions.MapInboundClaims = false;
});

JWT con varios esquemas

A menudo, las API necesitan dar cabida a los tokens de acceso de varios emisores. La compatibilidad con varios emisores de tokens en una API se puede realizar mediante:

  • APIs independientes: Crear APIs distintas con esquemas de autenticación dedicados para cada emisor.
  • AddPolicyScheme Este método puede definir varios esquemas de autenticación e implementar lógica para seleccionar el esquema adecuado en función de las propiedades del token (por ejemplo, emisor, notificaciones). Este enfoque permite una mayor flexibilidad dentro de una sola API.

Forzar la autenticación de portador

SetDefaultPolicy se puede usar para requerir autenticación para todas las solicitudes incluso a los puntos de conexión sin un atributo [Authorize]. SetDefaultPolicy configura la directiva que se usa para los puntos de conexión con el atributo [Authorize] y ya tiene como valor predeterminado requerir usuarios autenticados. Consulte la documentación sobre la necesidad de usuarios autenticados para obtener más detalles.

C#
var requireAuthPolicy = new AuthorizationPolicyBuilder()
	.RequireAuthenticatedUser()
	.Build();

builder.Services.AddAuthorizationBuilder()
	.SetDefaultPolicy(requireAuthPolicy);

El atributo Authorize también se puede usar para forzar la autenticación. Si se usan varios esquemas, el esquema de portador generalmente debe establecerse como esquema de autenticación predeterminado o especificado a través de [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme]).

Autorización en controladores:

C#
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{

Autorización en API mínimas:

C#
app.MapGet("/hello", [Authorize] () => "Hi");

El control inseguro de los tokens de acceso, como la autenticación débil o el almacenamiento de tokens en el almacenamiento vulnerable del lado cliente, pueden provocar importantes vulnerabilidades de seguridad. Por ejemplo, almacenar tokens de acceso directamente en el navegador mediante almacenamiento local, almacenamiento de sesión o roles de trabajo. La siguiente sección contiene procedimientos recomendados para las aplicaciones que usan y crean tokens de acceso.

Uso de estándares

Se deben usar siempre estándares como OpenID Connect o OAuth al crear tokens de acceso. Los tokens de acceso no deben en aplicaciones de producción sin cumplir las precauciones de seguridad descritas en este artículo. La creación de tokens de acceso debe limitarse a escenarios de prueba.

Uso de claves asimétricas

Se deben usar siempre claves asimétricas al crear tokens de acceso. La clave pública está disponible en los puntos de conexión conocidos y los clientes de API pueden validar la firma del token de acceso mediante la clave pública.

Nunca cree un token de acceso a partir de una solicitud de nombre de usuario o contraseña.

No debe crear un token de acceso a partir de una solicitud de nombre de usuario y contraseña. Las solicitudes de nombre de usuario y contraseña no están autenticadas y son vulnerables a ataques de suplantación de identidad y phishing. Los tokens de acceso solo se deben crear mediante un flujo de OpenID Connect o un flujo estándar de OAuth. La desviación de estos estándares puede dar lugar a una aplicación no segura.

Usar cookies

Para aplicaciones web seguras, se requiere un back-end para almacenar tokens de acceso en un servidor de confianza. Solo se comparte una cookie segura HTTP en el navegador del cliente. Consulte la documentación de autenticación de OIDC para obtener información sobre cómo hacerlo en una aplicación web de ASP.NET Core.

API de bajada

En ocasiones, las API necesitan acceder a los datos de usuario desde las API de bajada en nombre del usuario autenticado en la aplicación que realiza la llamada. Aunque la implementación de un flujo de credenciales de cliente de OAuth es una opción, requiere plena confianza entre las dos aplicaciones de API. Un enfoque más seguro implica el uso de una estrategia de confianza cero con un token de acceso de usuario delegado. Este enfoque:

  • Mejora la seguridad concediéndole a la API solo los permisos necesarios para ese usuario específico.
  • Requiere que la API cree el nuevo token de acceso para el usuario que llama a la aplicación y a la API.

Hay varias maneras de implementar una estrategia de confianza cero con un token de acceso de usuario delegado:

Uso del intercambio de tokens de OAuth 2.0 para solicitar un nuevo token de acceso delegado

Esta es una buena manera de implementar este requisito, pero es complicado si debe implementar el flujo de OAuth.

Consulte Intercambio de tokens de OAuth 2.0

Uso del flujo con derechos delegados de la web de Microsoft Identity para solicitar un nuevo token de acceso delegado

El uso de la biblioteca de autenticación web de Microsoft Identity es el enfoque más sencillo y seguro. Solo funciona con Microsoft Entra ID y Microsoft Entra External ID.

Para obtener más información, consulte Plataforma de identidades de Microsoft y flujo con derechos delegados de OAuth 2.0.

Uso del mismo token de acceso delegado enviado a la API

Este enfoque no es difícil de implementar, pero el token de acceso tiene acceso a todas las API de bajada. El proxy inverso de Yarp se puede usar para implementarlo.

Uso del flujo de credenciales de cliente de OAuth y uso de un token de acceso a la aplicación

Esto es fácil de implementar, pero la aplicación cliente tiene acceso completo a la aplicación y no un token de acceso delegado. El token debe almacenarse en caché en la aplicación de API cliente.

Nota

Cualquier medida de seguridad entre aplicaciones también funciona. Se puede usar la autenticación de certificados o, en Azure, una identidad administrada.

Gestión de tokens de acceso

Al usar tokens de acceso en una aplicación cliente, los tokens de acceso deben rotarse, conservarse y almacenarse en algún lugar del servidor. En una aplicación web, las cookies se utilizan para asegurar la sesión y pueden usarse para almacenar tokens mediante la opción SaveTokens.

SaveTokens actualmente no actualizará los tokens de acceso automáticamente, pero esta funcionalidad está prevista para .NET 10. Siga https://github.com/dotnet/aspnetcore/issues/8175 para obtener actualizaciones. Mientras tanto, puede actualizar manualmente el token de acceso como se muestra en la documentación de Blazor Web App con OIDC o usar un paquete NuGet de terceros como Duende.AccessTokenManagement.OpenIdConnect para controlar y administrar tokens de acceso en la aplicación cliente. Para obtener más información, consulte Administración de tokens de Duende.

Nota

Si se implementa en producción, la memoria caché debe funcionar en una implementación de varias instancias. Normalmente se requiere una caché persistente.

Algunos servidores de tokens seguros cifran los tokens de acceso. Los tokens de acceso no requieren ningún formato. Al usar la introspección de OAuth, se usa un token de referencia en lugar de un token de acceso. Una aplicación cliente (UI) nunca debe abrir un token de acceso, ya que el token de acceso no está pensado para esto. Solo una API para la que se creó el token de acceso debe abrir el token de acceso.

  • No se deben abrir tokens de acceso en una aplicación de interfaz de usuario
  • No envíe el token de identificador a las API.
  • Los tokens de acceso pueden tener cualquier formato
  • Los tokens de acceso se pueden cifrar
  • Los tokens de acceso expiran y deben rotarse
  • Los tokens de acceso se conservan en un servidor back-end seguro

YARP (Yet Another Reverse Proxy)

YARP (Yet Another Reverse Proxy) es una buena herramienta para controlar las solicitudes HTTP y reenviar las solicitudes a otras API. YARP puede implementar la lógica de seguridad para adquirir nuevas credenciales de acceso. YARP se usa con frecuencia en la arquitectura de seguridad de back-end a front-end (BFF). La documentación de Blazor Web App con OIDC muestra el uso de YARP para implementar el patrón BFF.

Pruebas de APIs

Las pruebas de integración y los contenedores con tokens de acceso se pueden usar para probar API seguras. Los tokens de acceso se pueden crear mediante la herramienta user-jwts de dotnet.

Advertencia

Asegúrese de que los problemas de seguridad no sean introducidos en la API con fines de prueba. Las pruebas se vuelven más difíciles cuando se usan tokens de acceso delegado, ya que estos tokens solo se pueden crear a través de una interfaz de usuario y un flujo de OpenID Connect. Si se usa una herramienta de prueba para crear tokens de acceso delegado, las características de seguridad deben deshabilitarse para las pruebas. Es esencial que estas características solo estén deshabilitadas en el entorno de prueba.

Cree entornos de prueba dedicados y aislados en los que las características de seguridad se puedan deshabilitar o modificar de forma segura. Asegúrese de que estos cambios estén estrictamente limitados al entorno de prueba.

Uso de la interfaz de usuario de Swagger, Curl y otras herramientas de interfaz de usuario de API

La interfaz de usuario de Swagger y Curl son excelentes herramientas de interfaz de usuario para probar las API. Para que las herramientas funcionen, la API puede generar un documento de OpenAPI que se puede cargar en la herramienta de pruebas de cliente. Se puede agregar un flujo de seguridad para adquirir un nuevo token de acceso al archivo OpenAPI de API.

Advertencia

No implemente flujos de prueba de seguridad no seguros en producción.

Al implementar una interfaz de usuario de Swagger para una API, normalmente no debe implementar la interfaz de usuario en producción, ya que la seguridad debe estar debilitada para permitir que esto funcione.

Asignación de notificaciones de OpenID Connect

Consulte el documento siguiente:

Asignación, personalización y transformación de notificaciones en ASP.NET Core

Estándares

JSON Web Token (JWT)

El marco de autorización de OAuth 2.0.

Demostración de prueba de posesión (DPoP) de OAuth 2.0

OAuth 2.0 JWT-Secured Solicitud de Autorización (JAR) RFC 9101

Autenticación de cliente de TLS mutua de OAuth 2.0 y tokens de acceso con certificado

OpenID Connect 1.0

Plataforma de identidades de Microsoft y flujo con derechos delegados de OAuth 2.0

Intercambio de tokens de OAuth 2.0

Perfil de JSON Web Token (JWT) para tokens de acceso de OAuth 2.0

Semántica HTTP RFC 9110