Lire en anglais

Partager via


Configurer l'authentification du porteur JWT dans ASP.NET Core

Par Damien Bowden

L'authentification par porteur JWT (jeton Web JSON) est couramment utilisée pour les API. Bien qu'il fonctionne de manière similaire à l'authentification cookie, le fournisseur d'identité émet un JWT ou des jetons en cas d'authentification réussie. Ces tokens peuvent ensuite être envoyés à d'autres serveurs pour s'authentifier, contrairement aux cookies qui ne sont renvoyés qu'au domaine émetteur. Un JWT est un jeton autonome qui encapsule des informations pour une ressource API ou un client. Le client qui a demandé le JWT peut demander des données à partir d’une ressource d’API à l’aide de l’en-tête d’autorisation et d’un jeton de type 'bearer'.

L'authentification par porteur JWT fournit :

  • Authentification: Lorsque vous utilisez le JwtBearerHandler, les jetons porteur sont essentiels pour l’authentification. Le JwtBearerHandler valide le jeton et extrait l'identité de l'utilisateur de ses revendications.
  • Autorisation : Les jetons porteur permettent l’autorisation en fournissant un ensemble de revendications représentant les autorisations de l’utilisateur ou de l’application, à l’image d’un cookie.
  • Autorisation déléguée: Lorsqu’un jeton d’accès spécifique à un utilisateur est utilisé pour authentifier entre des API au lieu d’un jeton d’accès valable pour toute l’application, ce processus est appelé autorisation déléguée.

Pour une introduction à l'authentification par jeton JWT, consultez JSON Web Tokens.Afficher ou télécharger des exemples de code

Cet article couvre les domaines suivants :

  • Types de jetons
  • Utilisation des jetons JWT pour sécuriser une API
  • Comment OIDC/OAuth s'inscrit-il dans ce contexte ?
  • Implémentation de l'authentification par jeton JWT
  • Approches recommandées pour créer un JWT

Types de jetons

Il existe de nombreux types de jetons et de formats. Il est déconseillé de générer vos propres jetons d'accès ou jetons ID, sauf à des fins de test. Les jetons auto-créés qui ne respectent pas les normes établies :

  • peuvent entraîner des failles de sécurité
  • Ils ne conviennent qu'aux systèmes fermés.

Nous vous recommandons d'utiliser OpenID Connect 1.0 ou une norme OAuth pour créer des jetons d'accès destinés à l'accès à l'API.

Jetons d’accès

Jetons d'accès :

  • Sont des chaînes utilisées par une application cliente pour effectuer des requêtes auprès du serveur mettant en œuvre une API.
  • Leur format peut varier. Différentes API peuvent utiliser différents formats pour les jetons.
  • Elles peuvent être chiffrées.
  • Ils ne doivent jamais être lus ou interprétés par un client Web ou une application d'interface utilisateur détenant le jeton d'accès.
  • Ils sont uniquement destinés à effectuer des requêtes auprès d'une API.
  • Ils sont généralement envoyés à l’API dans l’en-tête de requête Autorisation sous forme de jeton porteur.

Voir le framework d'autorisation OAuth 2.0

Jetons d'accès d'application et jetons d'accès délégué

Les jetons d'accès peuvent être des jetons d'accès d'application ou des jetons d'accès délégué. Les jetons ont des revendications différentes et sont gérés et stockés différemment. Un jeton d'accès d'application est généralement stocké une fois dans l'application jusqu'à ce qu'il expire, tandis qu'un jeton d'accès délégué est stocké par utilisateur, soit dans un cookie, soit dans un cache de serveur sécurisé.

Nous vous recommandons d'utiliser des jetons d'accès d'utilisateur délégué chaque fois qu'un utilisateur est impliqué. Les API en aval peuvent faire la requête d'un jeton d'accès d'utilisateur délégué au nom de l'utilisateur authentifié.

Jetons d’accès limités à l’expéditeur

Les jetons d’accès peuvent être utilisés en tant que jetons porteur ou jetons limités à l’expéditeur pour accéder aux ressources. Les jetons limités à l'expéditeur exigent que le client qui en fait la requête prouve qu'il possède une clé privée pour pouvoir utiliser le jeton. Le fait de prouver la possession d'une clé privée garantit que le jeton ne peut pas être utilisé indépendamment. Les jetons limités à l'expéditeur peuvent être mis en œuvre de deux manières :

Jetons d’ID

Les jetons ID sont des jetons de sécurité qui confirment l'authentification réussie d'un utilisateur. Les jetons permettent au client de vérifier l'identité de l'utilisateur. Le serveur de jeton JWT émet des jetons d'ID contenant des assertions avec des informations sur l'utilisateur. Les jetons ID sont toujours au format JWT.

Les jetons ID ne doivent jamais être utilisés pour accéder aux API.

Autres jetons

Il existe de nombreux types de jetons, notamment les jetons d'accès et les jetons d'ID, comme spécifié par les normes OpenID Connect et OAuth. Les jetons d'actualisation peuvent être utilisés pour actualiser une application d'interface utilisateur sans réauthentifier l'utilisateur. Les jetons OAuth JAR peuvent envoyer de manière sécurisée des demandes d’autorisation. Les flux d'informations d'identification vérifiables utilisent des types de JWT pour émettre ou vérifier des informations d'identification. Il est essentiel d'utiliser les jetons conformément aux spécifications. Pour plus d'informations, reportez-vous aux liens vers les normes fournis plus loin dans cet article.

Utilisation de jetons JWT pour sécuriser une API

Lorsque vous utilisez des jetons d'accès JWT pour l'autorisation de l'API, l'API accorde ou refuse l'accès en fonction du jeton fourni. Si la requête n'est pas autorisée, une réponse 401 ou 403 est renvoyée. L'API ne doit pas rediriger l'utilisateur vers le fournisseur d'identité pour obtenir un nouveau jeton ou demander des permissions supplémentaires. L'application qui consomme l'API est responsable de l'acquisition d'un jeton approprié. Cela garantit une séparation claire des préoccupations entre l'API (autorisation) et l'application cliente consommatrice (authentification).

Notes

HTTP permet également de renvoyer 404 pour non autorisé, afin de ne pas divulguer d'informations sur l'existence de ressources à des clients non autorisés.

401 Non autorisé

Une réponse 401 non autorisée indique que le jeton d'accès fourni ne répond pas aux normes requises. Cela peut être dû à plusieurs raisons, notamment

  • Signature non valide : La signature du jeton ne correspond pas, ce qui suggère une éventuelle falsification.
  • Expiration : Le jeton a expiré et n'est plus valide.
  • Revendications incorrectes: Les revendications critiques dans le jeton, comme l’audience (aud) ou l’émetteur (iss), sont manquantes ou invalides.

Notes

Extrait de la RFC 9110 sur la sémantique HTTP : Le serveur générant une réponse 401 DOIT envoyer un en-tête WWW-Authenticate (section 11.6.1) contenant au moins un challenge applicable à la ressource cible.

Les spécifications OAuth fournissent des lignes directrices détaillées sur les revendications requises et leur validation.

403 Interdit

Une réponse 403 Forbidden indique généralement que l'utilisateur authentifié ne dispose pas des permissions nécessaires pour accéder à la ressource demandée. Ceci est distinct des problèmes d'authentification, par exemple un jeton invalide, et n'est pas lié aux revendications standard au sein du jeton d'accès.

Dans ASP.NET Core, vous pouvez appliquer l'autorisation à l'aide de :

Des exigences et des stratégies : Définissez des exigences personnalisées, par exemple « Doit être un administrateur » et associez-les à des stratégies. Autorisation basée sur les rôles : Attribuez des utilisateurs à des rôles, par exemple « Administrateur », « Éditeur », et limitez l'accès en fonction de ces rôles.

Quel est le rôle de l'OIDC et/ou de l'OAuth lors de l'utilisation de jetons de support ?

Lorsqu'une API utilise des jetons d'accès JWT pour l'autorisation, l'API ne valide que le jeton d'accès, et non la manière dont il a été obtenu.

OpenID Connect (OIDC) et OAuth 2.0 fournissent des frameworks normalisés et sécurisés pour l'acquisition de jetons. L'acquisition de jetons varie en fonction du type d'application. En raison de la complexité de l'acquisition sécurisée de jetons, il est fortement recommandé de s'appuyer sur ces normes :

  • Pour les apps agissant au nom d'un utilisateur et d'une application : OIDC est le choix préféré, permettant un accès délégué à l'utilisateur. Dans les applications Web, le flux de code confidentiel avec la clé de preuve pour l'échange de code (PKCE) est recommandé pour une sécurité accrue.
  • Si l'application n'a pas d'utilisateur : Le flux d'informations d'identification du client OAuth 2.0 convient pour obtenir des jetons d'accès à l'application

Implémentation de l'authentification par jeton JWT

Le package Nuget Microsoft.AspNetCore.Authentication.JwtBearer peut être utilisé pour valider les jetons porteur JWT.

Les jetons JWT doivent être entièrement validés dans une API. Les éléments suivants doivent être validés :

  • Signature, pour la confiance et l’intégrité. Cela permet de s'assurer que le jeton a été créé par le service de jetons sécurisé désigné et qu'il n'a pas été altéré.
  • La revendication de l’émetteur avec la valeur attendue.
  • La revendication de l’audience avec la valeur attendue.
  • L'expiration du jeton.

Les revendications suivantes sont requises pour les jetons d'accès OAuth 2.0 : iss, exp, aud, sub, client_id, iat et jti.

Si l'une de ces affirmations ou valeurs est incorrecte, l'API doit renvoyer une réponse 401.

Validation de base du jeton porteur JWT

Une implémentation de base de AddJwtBearer peut valider uniquement l’audience et l’émetteur. La signature doit être validée pour que le jeton soit fiable et qu'il n'ait pas été falsifié.

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

Validation explicite du jeton porteur JWT

La méthode AddJwtBearer offre plusieurs configurations. Certains fournisseurs de jetons sécurisés utilisent une adresse de métadonnées non standard et le paramètre peut être configuré explicitement. L'API peut accepter plusieurs émetteurs ou audiences.

Il n'est pas nécessaire de définir explicitement les paramètres. Les définitions dépendent des valeurs de réclamation du jeton d'accès et du serveur de jetons sécurisés utilisé pour valider le jeton d'accès. Vous devez utiliser les valeurs par défaut si possible.

Veuillez consulter les détails de Mappage des revendications MapInboundClaims.

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 avec plusieurs schémas

Les API doivent souvent prendre en charge des jetons d'accès provenant de différents émetteurs. La prise en charge de plusieurs émetteurs de jetons dans une API peut se faire de la manière suivante :

  • API distinctes : Créez des API distinctes avec des schémas d'authentification dédiés à chaque émetteur.
  • AddPolicyScheme Cette méthode permet de définir plusieurs schémas d'authentification et de mettre en œuvre une logique de sélection du schéma approprié en fonction des propriétés du jeton (par exemple, l'émetteur, les revendications) Cette approche permet une plus grande flexibilité au sein d'une API unique

Forcer l'authentification du porteu

SetDefaultPolicy peut être utilisé pour exiger l'authentification de toutes les requêtes, même pour les endpoints sans attribut [Authorize]. SetDefaultPolicy configure la politique utilisée pour les points de terminaison avec l’attribut [Authorize] et, par défaut, elle exige déjà des utilisateurs authentifiés. Veuillez consulter la documentation sur les utilisateurs authentifiés requis pour plus de détails.

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

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

L'attribut Authorize peut également être utilisé pour forcer l'authentification Si plusieurs schémas sont utilisés, le schéma du porteur doit généralement être défini comme schéma d'authentification par défaut ou spécifié via [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme])

Autorisation dans les contrôleurs

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

Autorisation dans les API minimales

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

Le traitement non sécurisé des jetons d'accès, tel qu'une authentification faible ou le stockage des jetons dans un espace de stockage vulnérable côté client, peut entraîner d'importantes failles de sécurité. Par exemple, stocker directement les jetons d’accès dans le navigateur à l’aide du stockage local, du stockage de session ou des web workers. La section suivante contient les meilleures pratiques pour les applications utilisant et créant des jetons d'accès.

Normes d'utilisation

Des standards comme OpenID Connect ou OAuth devraient toujours être utilisés lors de la création de jetons d’accès. Les jetons d’accès ne devraient pas être créés dans les applications de production sans respecter les précautions de sécurité décrites dans cet article. La création de jetons d'accès doit être limitée à des scénarios de test.

Utilisez des clés asymétriques

Les clés asymétriques devraient toujours être utilisées lors de la création de jetons d’accès. La clé publique est disponible dans les points de terminaison bien connus et les clients de l'API peuvent valider la signature du jeton d'accès à l'aide de la clé publique.

Ne créez jamais de jeton d'accès à partir d'une requête de type nom d'utilisateur/mot de passe.

Vous ne devriez NE PAS créer de jeton d’accès à partir d’une requête utilisant un nom d’utilisateur et un mot de passe. Les requêtes de nom d'utilisateur/mot de passe ne sont pas authentifiées et sont vulnérables aux attaques par emprunt d'identité et par hameçonnage. Les jetons d'accès ne doivent être créés qu'à l'aide d'un flux OpenID Connect ou d'un flux standard OAuth. Si vous vous écartez de ces normes, vous risquez d'avoir une application non sécurisée.

Utiliser les cookies

Pour les applications Web sécurisées, un backend est nécessaire pour stocker les jetons d'accès sur un serveur de confiance. Seul un cookie HTTP only sécurisé est partagé sur le navigateur du client. Consultez la documentation sur l'authentification OIDC pour savoir comment procéder dans une application Web ASP.NET Core.

API en aval

Les API doivent occasionnellement accéder aux données utilisateur des API en aval pour le compte de l'utilisateur authentifié dans l'application appelante. L’implémentation d’un flux OAuth avec des informations d'identification client est une option qui nécessite une confiance totale entre les deux applications API. Une approche plus sûre consiste à utiliser une stratégie de confiance zéro avec un jeton d'accès utilisateur délégué. Cette approche a les caractéristiques suivantes :

  • Renforce la sécurité en accordant à l'API uniquement les permissions nécessaires pour cet utilisateur spécifique.
  • L'API doit créer un nouveau jeton d'accès pour l'utilisateur qui appelle l'application et l'API.

Il existe plusieurs façons de mettre en œuvre une stratégie de confiance zéro avec un jeton d'accès d'utilisateur délégué :

Utilisez l'échange de jetons OAuth 2.0 pour demander un nouveau jeton d'accès délégué.

C'est une bonne façon de mettre en œuvre cette exigence, mais elle est compliquée si vous devez mettre en œuvre le flux OAuth.

Voir échange de jetons OAuth 2.0

Utiliser Microsoft Identity Web au nom du flux pour demander un nouveau jeton d'accès délégué

L'utilisation de la bibliothèque d'authentification MicrosoftIdentity Web est l'approche la plus simple et la plus sûre. Il fonctionne uniquement avec Microsoft Entra ID et Microsoft Entra External ID.

Pour plus d'informations, voir [Plate-forme d'identité Microsoft et flux OAuth 2.0 On-Behalf-Of].

Utiliser le même jeton d'accès délégué envoyé à l'API

Cette approche n'est pas difficile à mettre en œuvre, mais le jeton d'accès a accès à toutes les API en aval. Le proxy inverse Yarp peut être utilisé pour mettre cela en œuvre.

Utiliser le flux d'informations d'identification du client OAuth et utiliser un jeton d'accès à l'application

Cette solution est facile à mettre en œuvre, mais l'application cliente dispose d'un accès complet à l'application et non d'un jeton d'accès délégué. Le jeton doit être mis en cache dans l'application API du client.

Notes

Toute sécurité d'application à application fonctionne également. L'authentification par certificat ou, dans Azure, une identité gérée peut être utilisée.

Gestion des jetons d'accès

Lors de l'utilisation de jetons d'accès dans une application cliente, les jetons d'accès doivent faire l'objet d'une rotation, être conservés et stockés quelque part sur le serveur. Dans une application Web, les cookies sont utilisés pour sécuriser la session et peuvent être utilisés pour stocker les jetons via l'option SaveTokens.

SaveTokens Actuellement, le serveur ne rafraîchit pas automatiquement les jetons d'actualisation, mais cette fonctionnalité est prévue pour XXX.NET 10. Suivez https://github.com/dotnet/aspnetcore/issues/8175 pour les mises à jour. En attendant, vous pouvez actualiser manuellement le jeton d'accès comme démontré dans la documentation Blazor Web App with OIDC ou utiliser un package NuGet tiers comme Duende.AccessTokenManagement.OpenIdConnect pour traiter et gérer les jetons d'accès dans l'application client. Pour plus d’informations, consultez Duende token management.

Notes

Si vous effectuez un déploiement en production, le cache doit fonctionner dans un déploiement multi-instance. Un cache persistant est normalement nécessaire.

Certains serveurs de jetons sécurisés chiffrent les jetons d'accès. Les jetons d'accès ne nécessitent aucun format. Lorsque vous utilisez l'introspection OAuth, un jeton de référence est utilisé à la place d'un jeton d'accès. Une application client (interface utilisateur) ne doit jamais ouvrir un jeton d'accès car celui-ci n'est pas destiné à cet usage. Seule une API pour laquelle le jeton d'accès a été créé doit ouvrir le jeton d'accès.

  • N'ouvrez pas les jetons d'accès dans une application d'interface utilisateur
  • N'envoyez pas le jeton ID aux API.
  • Les jetons d'accès peuvent avoir n'importe quel format
  • Les jetons d'accès peuvent être chiffrés.
  • Les jetons d'accès expirent et doivent être renouvelés.
  • Les jetons d'accès sont conservés sur un serveur sécurisé.

YARP (Yet Another Reverse Proxy)

YARP (Yet Another Reverse Proxy) est un bon outil pour traiter les requêtes HTTP et les rediriger vers d'autres API. YARP peut mettre en œuvre une logique de sécurité pour l'acquisition de nouvelles références d'accès. YARP est fréquemment utilisé dans l’architecture de sécurité backend for frontend (BFF). La Blazor Web App avec la documentation OIDC démontre l’utilisation de YARP pour implémenter le pattern BFF.

Test d’API

Les tests d'intégration et les conteneurs avec jetons d'accès peuvent être utilisés pour tester des API sécurisées. Les jetons d'accès peuvent être créés à l'aide de l'outil dotnet user-jwts.

Avertissement

Veillez à ce que les problèmes de sécurité ne soient pas introduits dans l'API à des fins de test. Les tests deviennent plus difficiles lorsque des jetons d'accès délégués sont utilisés, car ces jetons ne peuvent être créés que par le biais d'une interface utilisateur et d'un flux OpenID Connect. Si un outil de test est utilisé pour créer des jetons d'accès délégué, les fonctionnalités de sécurité doivent être désactivées pour les tests. Il est essentiel que ces fonctionnalités ne soient désactivées que dans l'environnement de test.

Créez des environnements de test dédiés et isolés où les fonctionnalités de sécurité peuvent être désactivées ou modifiées en toute sécurité. Veillez à ce que ces changements soient strictement limités à l'environnement de test.

Utilisez Swagger UI, Curl et d'autres outils d'interface utilisateur d'API

Swagger UI et Curl sont d'excellents outils d'interface utilisateur pour tester les API. Pour que ces outils fonctionnent, l'API peut produire un document OpenAPI qui peut être chargé dans l'outil de test client. Un flux de sécurité permettant d'acquérir un nouveau jeton d'accès peut être ajouté au fichier OpenAPI de l'API.

Avertissement

Ne déployez pas de flux de test de sécurité non sécurisés dans la production.

Lorsque vous mettez en œuvre une interface utilisateur Swagger pour une API, vous ne devez normalement pas déployer l'interface utilisateur en production, car la sécurité doit être affaiblie pour que cela fonctionne.

Mapper les revendications d’OpenID Connect

Reportez-vous au document suivant :

Mappage, personnalisation et transformation des réclamations dans ASP.NET Core

Standards

Jeton Web JSON (JWT)

Le framework d'autorisation OAuth 2.0

OAuth 2.0 démontrant la preuve de possession DPoP

Demande d'Autorisation JWT-Secured OAuth 2.0 (JAR) RFC 9101

Authentification client Mutual-TLS OAuth 2.0 et jetons d'accès liés à un certificat

OpenID Connect 1.0

Plate-forme d'identité Microsoft et flux OAuth 2.0 On-Behalf-Of

Échange de jeton OAuth 2.0

Profil JSON Web Token (JWT) pour les jetons d'accès OAuth 2.0

HTTP Semantics RFC 9110