Partager via


Sécuriser une Blazor Web App ASP.NET Core avec OpenID Connect (OIDC)

Cet article explique comment sécuriser une Blazor Web App avec OpenID Connect (OIDC) en utilisant un exemple d’application du référentiel GitHub dotnet/blazor-samples (.NET 8 ou ultérieur) (comment télécharger).

Cette version de l’article traite de l’implémentation d’OIDC sans adopter le Modèle BFF (Backend for Frontend). Le modèle BFF est pratique pour faire des requêtes authentifiées auprès de services externes. Changez le sélecteur de version de l’article en OIDC avec le modèle BFF si la spécification de l’application appelle à adopter le modèle BFF.

La spécification suivante est couverte :

  • L’Blazor Web App utilise le mode de rendu automatique avec interactivité globale.
  • Les services de fournisseur d’état d’authentification personnalisée sont utilisés par les applications serveur et clientes pour capturer l’état d’authentification de l’utilisateur et le transmettre entre le serveur et le client.
  • Cette application est un point de départ pour les flux d’authentification OIDC. OIDC est configuré manuellement dans l’application et ne s’appuie pas sur des packages Microsoft Entra ID ou Microsoft Identity Web, et l’exemple d’application ne nécessite pas d’hébergement Microsoft Azure. L’exemple d’application peut cependant être utilisé avec Entra ou Microsoft Identity Web, et être hébergé dans Azure.
  • Actualisation automatique des jetons non interactifs.
  • Appelle de façon sécurisée une API (web) dans le projet de serveur pour les données.

Exemple d’application

Cet exemple d’application est composé de deux projets :

  • BlazorWebAppOidc : projet côté serveur de l’Blazor Web App, contenant un exemple de point de terminaison d’API minimale pour des données météorologiques.
  • BlazorWebAppOidc.Client : projet côté client de l’Blazor Web App.

Accédez aux exemples d’applications par le biais du dossier de version le plus récent à partir de la racine du référentiel avec le lien suivant. Les projets se trouvent dans le dossier BlazorWebAppOidc pour .NET 8 ou ultérieur.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Projet de l’Blazor Web App côté serveur (BlazorWebAppOidc)

Le projet BlazorWebAppOidc est le projet côté serveur de l’Blazor Web App.

Le fichier BlazorWebAppOidc.http peut être utilisé pour tester la requête de données météorologiques. Notez que le projet BlazorWebAppOidc doit être en cours d’exécution pour tester le point de terminaison et que le point de terminaison est codé en dur dans le fichier. Pour en savoir plus, reportez-vous à Utiliser des fichiers .http dans Visual Studio 2022.

Remarque

Le projet de serveur utilise IHttpContextAccessor/HttpContext, mais jamais pour les composants affichés de manière interactive. Pour plus d’informations, consultez Guide d’atténuation des menaces pour l’affichage interactif côté serveur de Blazor ASP.NET Core.

Configuration

Cette section explique comment configurer l’exemple d’application.

Remarque

Pour Microsoft Entra ID ou Azure AD B2C, vous pouvez utiliser AddMicrosoftIdentityWebApp à partir de Microsoft Identity Web (Microsoft.Identity.Web package NuGet, documentation de l’API), qui ajoute à la fois les gestionnaires OIDC et Cookie d’authentification avec les valeurs par défaut appropriées. L’exemple d’application et l’aide de cette section n’utilisent pas Microsoft Identity Web. L’aide montre comment configurer manuellement le gestionnaire OIDC pour n’importe quel fournisseur OIDC. Pour plus d’informations sur l’implémentation de Microsoft Identity Web, consultez les ressources liées.

Établir la clé secrète client

Avertissement

Ne stockez pas les secrets d’application, les chaîne de connexion, les informations d’identification, les mots de passe, les numéros d’identification personnels (PIN), le code C#/.NET privé ou les clés/jetons privés dans le code côté client, qui est toujours non sécurisé. Dans les environnements de test/intermédiaire et de production, le code côté Blazor serveur et les API web doivent utiliser des flux d’authentification sécurisés qui évitent de conserver les informations d’identification dans le code du projet ou les fichiers de configuration. En dehors des tests de développement locaux, nous vous recommandons d’éviter l’utilisation de variables d’environnement pour stocker des données sensibles, car les variables d’environnement ne sont pas l’approche la plus sécurisée. Pour les tests de développement locaux, l’outil Secret Manager est recommandé pour sécuriser les données sensibles. Pour plus d’informations, consultez Gestion sécurisée des données sensibles et des informations d’identification.

Pour les tests de développement locaux, utilisez l’outil Gestionnaire de secrets pour stocker la clé secrète client de l’application serveur sous la clé Authentication:Schemes:MicrosoftOidc:ClientSecretde configuration.

Remarque

Si l’application utilise l’ID Microsoft Entra ou Azure AD B2C, créez une clé secrète client dans l’inscription de l’application dans l’Entra ou Portail Azure (Gérer les>certificats et secrets>Nouveau secret client). Utilisez la valeur du nouveau secret dans les instructions suivantes.

L’exemple d’application n’a pas été initialisé pour l’outil Secret Manager. Utilisez un interpréteur de commandes, tel que l’interpréteur de commandes Developer PowerShell dans Visual Studio, pour exécuter la commande suivante. Avant d’exécuter la commande, remplacez le répertoire par le cd répertoire du projet de serveur. La commande établit un identificateur de secrets utilisateur (<UserSecretsId> dans le fichier projet de l’application serveur) :

dotnet user-secrets init

Exécutez la commande suivante pour définir la clé secrète client. L’espace {SECRET} réservé est la clé secrète client obtenue à partir de l’inscription de l’application :

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Si vous utilisez Visual Studio, vous pouvez confirmer que le secret est défini en cliquant avec le bouton droit sur le projet de serveur dans Explorateur de solutions et en sélectionnant Gérer les secrets utilisateur.

Configurer l’application

La configuration OpenIdConnectOptions suivante se trouve dans le fichier Program du projet sur l’appel à AddOpenIdConnect :

  • SignInScheme : définit le schéma d’authentification correspondant au middleware responsable de la conservation de l’identity de l’utilisateur après une authentification réussie. Le gestionnaire OIDC doit utiliser un schéma de connexion capable de conserver les informations d’identification de l’utilisateur entre les requêtes. La ligne suivante est présente seulement à des fins de démonstration. Si elle est omise, DefaultSignInScheme est utilisé comme valeur de secours.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Étendues pour openid et profile (Scope) (Facultatif) : les étendues openid et profile sont également configurées par défaut, car elles sont nécessaires pour que le gestionnaire OIDC fonctionne, mais il peut être nécessaire de les ajouter à nouveau si des étendues sont incluses dans la configuration de Authentication:Schemes:MicrosoftOidc:Scope. Pour obtenir une aide générale sur la configuration, consultez Configuration dans ASP.NET Core et Configuration d’ASP.NET Core Blazor.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens : définit si les jetons d’accès et d’actualisation doivent être stockés dans les AuthenticationProperties après une autorisation réussie. Cette propriété est définie par défaut sur false pour réduire la taille du cookie d’authentification finale.

    oidcOptions.SaveTokens = false;
    
  • Étendue de l’accès hors connexion (Scope) : l’étendue offline_access est nécessaire pour le jeton d’actualisation.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Authority et ClientId : définit l’autorité et l’ID client pour les appels OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Exemple :

    • Autorité ({AUTHORITY}) : https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (utilise l’ID de locataire aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • ID client ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Exemple pour l’autorité « common » de Microsoft Azure :

    L’autorité « common » doit être utilisée pour les applications multilocataires. Vous pouvez également utiliser l’autorité « common » pour les applications monolocataires, mais un IssuerValidator personnalisé est nécessaire, comme indiqué plus loin dans cette section.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType : configure le gestionnaire OIDC pour effectuer seulement le flux du code d’autorisation. Les octrois implicites et les flux hybrides ne sont pas nécessaires dans ce mode.

    Dans la configuration de l’inscription d’application Octroi implicite et flux hybrides du portail Entra ou Azure, ne cochez pas la case pour que le point de terminaison d’autorisation retourne des Jetons d’accès ou des Jetons d’ID. Le gestionnaire OIDC demande automatiquement les jetons appropriés en utilisant le code retourné par le point de terminaison d’autorisation.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims et configuration de NameClaimType et de RoleClaimType : de nombreux serveurs OIDC utilisent « name » et « role » au lieu des valeurs par défaut de SOAP/WS-Fed dans ClaimTypes. Quand MapInboundClaims est défini sur false, le gestionnaire n’effectue pas de mappages des revendications et les noms des revendications du JWT sont directement utilisés par l’application. L’exemple suivant définit le type de revendication de rôle sur « roles », qui est approprié pour Microsoft Entra ID (ME-ID). Consultez la documentation de votre fournisseur d’identity pour plus d’informations.

    Remarque

    MapInboundClaims doit être défini sur false pour la plupart des fournisseurs OIDC, ce qui empêche le renommage des revendications.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuration du chemin d’accès : les chemins d’accès doivent correspondre à l’URI de redirection (chemin d’accès du rappel de la connexion) et publier les chemins d’accès de redirection de déconnexion (chemin d’accès du rappel déconnecté) configurés lors de l’inscription de l’application auprès du fournisseur OIDC. Dans le portail Azure, les chemins d’accès sont configurés dans le panneau Authentification de l’inscription de l’application. Les chemins d’accès de connexion et de déconnexion doivent être enregistrés en tant qu’URI de redirection. Les valeurs par défaut sont /signin-oidc et /signout-callback-oidc.

    • CallbackPath : le chemin d’accès de la demande dans le chemin d’accès de base de l’application où l’agent utilisateur est retourné.

      Dans le portail Entra ou Azure, définissez le chemin d’accès dans l’URI de redirection de la configuration de la plateforme web :

      https://localhost/signin-oidc

      Remarque

      Un port n’est pas requis pour les adresses localhost lors de l’utilisation de Microsoft Entra ID. La plupart des autres fournisseurs OIDC nécessitent un port correct.

    • SignedOutCallbackPath : chemin d’accès de requête dans le chemin d’accès de base de l’application où l’agent utilisateur est renvoyé après la déconnexion du fournisseur d’identity.

      Dans le portail Entra ou Azure, définissez le chemin d’accès dans l’URI de redirection de la configuration de la plateforme web :

      https://localhost/signout-callback-oidc

      Remarque

      Un port n’est pas requis pour les adresses localhost lors de l’utilisation de Microsoft Entra ID. La plupart des autres fournisseurs OIDC nécessitent un port correct.

      Remarque

      Si vous utilisez Microsoft Identity Web, le fournisseur redirige actuellement seulement vers SignedOutCallbackPath si l’autorité microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/) est utilisée. Cette limitation n’existe pas si vous pouvez utiliser l’autorité « common » avec Microsoft Identity Web. Pour plus d’informations, consultez postLogoutRedirectUri ne fonctionne pas quand l’URL de l’autorité contient un ID de locataire (AzureAD/microsoft-authentication-library-for-js #5783).

    • RemoteSignOutPath : les demandes reçues sur ce chemin d’accès font que le gestionnaire appelle la déconnexion en utilisant le schéma de déconnexion.

      Dans le portail Entra ou Azure, définissez l’URL de déconnexion du canal frontal :

      https://localhost/signout-oidc

      Remarque

      Un port n’est pas requis pour les adresses localhost lors de l’utilisation de Microsoft Entra ID. La plupart des autres fournisseurs OIDC nécessitent un port correct.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Exemples (valeurs par défaut) :

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure uniquement avec le point de terminaison « common ») TokenValidationParameters.IssuerValidator : de nombreux fournisseurs OIDC fonctionnent avec le validateur émetteur par défaut, mais nous devons tenir compte de l’émetteur paramétré avec l’ID de locataire ({TENANT ID}) retourné par https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Pour plus d’informations, consultez SecurityTokenInvalidIssuerException avec OpenID Connect et le point de terminaison « common » d’Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

    Uniquement pour les applications utilisant Microsoft Entra ID ou Azure AD B2C avec le point de terminaison « common » :

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Exemple de code d’application

Examinez les fonctionnalités suivantes dans l’exemple d’application :

  • Actualisation automatique des jetons non interactifs avec l’aide d’un actualiseur cookie personnalisé (CookieOidcRefresher.cs).
  • Le projet serveur appelle AddAuthenticationStateSerialization pour ajouter un fournisseur d’état d’authentification côté serveur qui utilise PersistentComponentState pour transmettre l’état d’authentification au client. Le client appelle AddAuthenticationStateDeserialization à désérialiser et à utiliser l’état d’authentification passé par le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.
  • Un exemple de requête adressée à l’Blazor Web App pour des données météorologiques est géré par un point de terminaison d’API minimale (/weather-forecast) dans le fichier Program (Program.cs). Le point de terminaison demande une autorisation en appelant RequireAuthorization. Pour les contrôleurs que vous ajoutez au projet, ajoutez l’attribut [Authorize] au contrôleur ou à l’action.
  • L’application appelle de façon sécurisée une API (web) dans le projet de serveur pour les données météo :
    • Lors du rendu du composant Weather sur le serveur, le composant utilise le ServerWeatherForecaster sur le serveur pour obtenir les données météo directement (et non via un appel d’API web).
    • Lorsque le composant est rendu sur le client, le composant utilise l’implémentation du service ClientWeatherForecaster, qui utilise un HttpClient préconfiguré (dans le fichier Program du projet client) pour effectuer un appel d’API web au projet de serveur. Un point de terminaison d’API minimal (/weather-forecast) défini dans le fichier Program du projet de serveur obtient les données météo du ServerWeatherForecaster et retourne les données au client.
  • Actualisation automatique des jetons non interactifs avec l’aide d’un actualiseur cookie personnalisé (CookieOidcRefresher.cs).
  • La classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) est un AuthenticationStateProvider côté serveur qui utilise PersistentComponentState pour transmettre l’état de l’authentification au client, qui est ensuite fixé pour la durée de vie de l’application WebAssembly.
  • Un exemple de requête adressée à l’Blazor Web App pour des données météorologiques est géré par un point de terminaison d’API minimale (/weather-forecast) dans le fichier Program (Program.cs). Le point de terminaison demande une autorisation en appelant RequireAuthorization. Pour les contrôleurs que vous ajoutez au projet, ajoutez l’attribut [Authorize] au contrôleur ou à l’action.
  • L’application appelle de façon sécurisée une API (web) dans le projet de serveur pour les données météo :
    • Lors du rendu du composant Weather sur le serveur, le composant utilise le ServerWeatherForecaster sur le serveur pour obtenir les données météo directement (et non via un appel d’API web).
    • Lorsque le composant est rendu sur le client, le composant utilise l’implémentation du service ClientWeatherForecaster, qui utilise un HttpClient préconfiguré (dans le fichier Program du projet client) pour effectuer un appel d’API web au projet de serveur. Un point de terminaison d’API minimal (/weather-forecast) défini dans le fichier Program du projet de serveur obtient les données météo du ServerWeatherForecaster et retourne les données au client.

Pour plus d’informations sur les appels d’API (web) à l’aide d’abstractions de service dans les Blazor Web App, consultez Appeler une API web à partir d’une application ASP.NET Core Blazor.

Projet d’Blazor Web App côté client (BlazorWebAppOidc.Client)

Le projet BlazorWebAppOidc.Client est le projet côté client de l’Blazor Web App.

Le client appelle AddAuthenticationStateDeserialization à désérialiser et à utiliser l’état d’authentification passé par le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.

La classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) est un AuthenticationStateProvider côté client qui détermine l’état d’authentification de l’utilisateur en recherchant les données conservées dans la page quand elle a été rendue sur le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.

Si l’utilisateur doit se connecter ou se déconnecter, un rechargement complet de la page est nécessaire.

L’exemple d’application fournit un nom d’utilisateur et un e-mail seulement à des fins d’affichage. Il n’inclut pas de jetons qui s’authentifient auprès du serveur lors de requêtes ultérieures, celles-ci étant gérées séparément en utilisant un cookie qui est inclus dans les requêtes HttpClient envoyées au serveur.

Cette version de l’article traite de l’implémentation d’OIDC avec le modèle BFF (Backend for Frontend). Changez le sélecteur de version de l’article en OIDC sans le modèle BFF si la spécification de l’application n’appelle pas à adopter le modèle BFF.

La spécification suivante est couverte :

  • L’Blazor Web App utilise le mode de rendu automatique avec interactivité globale.
  • Les services de fournisseur d’état d’authentification personnalisée sont utilisés par les applications serveur et clientes pour capturer l’état d’authentification de l’utilisateur et le transmettre entre le serveur et le client.
  • Cette application est un point de départ pour les flux d’authentification OIDC. OIDC est configuré manuellement dans l’application et ne s’appuie pas sur des packages Microsoft Entra ID ou Microsoft Identity Web, et l’exemple d’application ne nécessite pas d’hébergement Microsoft Azure. L’exemple d’application peut cependant être utilisé avec Entra ou Microsoft Identity Web, et être hébergé dans Azure.
  • Actualisation automatique des jetons non interactifs.
  • Le modèle BFF (Backend for Frontend) est adopté en utilisant .NET Aspire pour la découverte de services et YARP pour proxyser les requêtes vers un point de terminaison de prévision météorologique sur l’application back-end.
    • Une API web back-end utilise l’authentification du porteur JWT pour valider les jetons JWT enregistrés par l’Blazor Web App dans le cookie de la connexion.
    • Aspire améliore l’expérience de création d’applications natives Cloud .NET. Il fournit un ensemble cohérent et validé d’outils et de modèles pour la création et l’exécution d’applications distribuées.
    • YARP (Yet Another Reverse Proxy) est une bibliothèque utilisée pour créer un serveur proxy inverse.

Pour plus d’informations sur .NET Aspire, consultez Disponibilité générale de .NET Aspire : simplifier le développement .NET cloud natif (mai 2024).

Prérequis

.NET Aspire nécessite la version 17.10 de Visual Studio ou une version ultérieure

Exemple d’application

L’exemple d’application est composé de cinq projets :

  • .NET Aspire:
    • Aspire.AppHost : utilisé pour gérer les problèmes d’orchestration généraux de l’application.
    • Aspire.ServiceDefaults : contient les configurations d’application .NET Aspire par défaut, qui peuvent être étendues et personnalisées en fonction des besoins.
  • MinimalApiJwt : API web back-end, contenant un exemple de point de terminaison d’API minimale pour les données météorologiques.
  • BlazorWebAppOidc : projet côté serveur de l’Blazor Web App.
  • BlazorWebAppOidc.Client : projet côté client de l’Blazor Web App.

Accédez aux exemples d’applications par le biais du dossier de version le plus récent à partir de la racine du référentiel avec le lien suivant. Les projets se trouvent dans le dossier BlazorWebAppOidcBff pour .NET 8 ou ultérieur.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

.NET Aspire projets

Pour plus d’informations sur l’utilisation de .NET Aspire et des détails sur les projets .AppHost et .ServiceDefaults de l’exemple d’application, consultez la documentation .NET Aspire.

Vérifiez que vous avez satisfait aux prérequis pour .NET Aspire. Pour plus d’informations, consultez la section Prérequis de Démarrage rapide : créer votre première application .NET Aspire.

L’exemple d’application configure uniquement un profil de lancement HTTP non sécurisé (http) à utiliser pendant les tests de développement. Pour plus d’informations, notamment un exemple de profils de paramètres de lancement non sécurisés et sécurisés, consultez Autoriser le transport non sécurisé dans .NET Aspire (documentation .NET Aspire).

Projet de l’Blazor Web App côté serveur (BlazorWebAppOidc)

Le projet BlazorWebAppOidc est le projet côté serveur de l’Blazor Web App. Le projet utilise YARP pour proxyser les requêtes vers un point de terminaison de prévision météorologique dans le projet d’API web back-end (MinimalApiJwt) avec le access_token stocké dans le cookie d’authentification.

Le fichier BlazorWebAppOidc.http peut être utilisé pour tester la requête de données météorologiques. Notez que le projet BlazorWebAppOidc doit être en cours d’exécution pour tester le point de terminaison et que le point de terminaison est codé en dur dans le fichier. Pour en savoir plus, reportez-vous à Utiliser des fichiers .http dans Visual Studio 2022.

Remarque

Le projet de serveur utilise IHttpContextAccessor/HttpContext, mais jamais pour les composants affichés de manière interactive. Pour plus d’informations, consultez Guide d’atténuation des menaces pour l’affichage interactif côté serveur de Blazor ASP.NET Core.

Configuration

Cette section explique comment configurer l’exemple d’application.

Remarque

Pour Microsoft Entra ID ou Azure AD B2C, vous pouvez utiliser AddMicrosoftIdentityWebApp à partir de Microsoft Identity Web (Microsoft.Identity.Web package NuGet, documentation de l’API), qui ajoute à la fois les gestionnaires OIDC et Cookie d’authentification avec les valeurs par défaut appropriées. L’exemple d’application et l’aide de cette section n’utilisent pas Microsoft Identity Web. L’aide montre comment configurer manuellement le gestionnaire OIDC pour n’importe quel fournisseur OIDC. Pour plus d’informations sur l’implémentation de Microsoft Identity Web, consultez les ressources liées.

Établir la clé secrète client

Avertissement

Ne stockez pas les secrets d’application, les chaîne de connexion, les informations d’identification, les mots de passe, les numéros d’identification personnels (PIN), le code C#/.NET privé ou les clés/jetons privés dans le code côté client, qui est toujours non sécurisé. Dans les environnements de test/intermédiaire et de production, le code côté Blazor serveur et les API web doivent utiliser des flux d’authentification sécurisés qui évitent de conserver les informations d’identification dans le code du projet ou les fichiers de configuration. En dehors des tests de développement locaux, nous vous recommandons d’éviter l’utilisation de variables d’environnement pour stocker des données sensibles, car les variables d’environnement ne sont pas l’approche la plus sécurisée. Pour les tests de développement locaux, l’outil Secret Manager est recommandé pour sécuriser les données sensibles. Pour plus d’informations, consultez Gestion sécurisée des données sensibles et des informations d’identification.

Pour les tests de développement locaux, utilisez l’outil Gestionnaire de secrets pour stocker la clé secrète client de l’application serveur sous la clé Authentication:Schemes:MicrosoftOidc:ClientSecretde configuration.

Remarque

Si l’application utilise l’ID Microsoft Entra ou Azure AD B2C, créez une clé secrète client dans l’inscription de l’application dans l’Entra ou Portail Azure (Gérer les>certificats et secrets>Nouveau secret client). Utilisez la valeur du nouveau secret dans les instructions suivantes.

L’exemple d’application n’a pas été initialisé pour l’outil Secret Manager. Utilisez un interpréteur de commandes, tel que l’interpréteur de commandes Developer PowerShell dans Visual Studio, pour exécuter la commande suivante. Avant d’exécuter la commande, remplacez le répertoire par le cd répertoire du projet de serveur. La commande établit un identificateur de secrets utilisateur (<UserSecretsId> dans le fichier projet de l’application serveur) :

dotnet user-secrets init

Exécutez la commande suivante pour définir la clé secrète client. L’espace {SECRET} réservé est la clé secrète client obtenue à partir de l’inscription de l’application :

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Si vous utilisez Visual Studio, vous pouvez confirmer que le secret est défini en cliquant avec le bouton droit sur le projet de serveur dans Explorateur de solutions et en sélectionnant Gérer les secrets utilisateur.

Configurer l’application

La configuration OpenIdConnectOptions suivante se trouve dans le fichier Program du projet sur l’appel à AddOpenIdConnect :

  • SignInScheme : définit le schéma d’authentification correspondant au middleware responsable de la conservation de l’identity de l’utilisateur après une authentification réussie. Le gestionnaire OIDC doit utiliser un schéma de connexion capable de conserver les informations d’identification de l’utilisateur entre les requêtes. La ligne suivante est présente seulement à des fins de démonstration. Si elle est omise, DefaultSignInScheme est utilisé comme valeur de secours.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Étendues pour openid et profile (Scope) (Facultatif) : les étendues openid et profile sont également configurées par défaut, car elles sont nécessaires pour que le gestionnaire OIDC fonctionne, mais il peut être nécessaire de les ajouter à nouveau si des étendues sont incluses dans la configuration de Authentication:Schemes:MicrosoftOidc:Scope. Pour obtenir une aide générale sur la configuration, consultez Configuration dans ASP.NET Core et Configuration d’ASP.NET Core Blazor.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens : définit si les jetons d’accès et d’actualisation doivent être stockés dans les AuthenticationProperties après une autorisation réussie. La valeur est définie sur true pour authentifier les requêtes de données météorologiques depuis le projet d’API web back-end (MinimalApiJwt).

    oidcOptions.SaveTokens = true;
    
  • Étendue de l’accès hors connexion (Scope) : l’étendue offline_access est nécessaire pour le jeton d’actualisation.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Étendues pour obtenir des données météorologiques depuis l’API web (Scope) : l’étendue Weather.Get est configurée dans le portail Azure ou Entra sous Exposer une API. Ceci est nécessaire pour que le projet d’API web back-end (MinimalApiJwt) valide le jeton d’accès avec le porteur JWT.

    oidcOptions.Scope.Add("{APP ID URI}/{API NAME}");
    

    Exemple :

    • URI ID D’application : ({APP ID URI}) : https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
      • Nom du répertoire : ({DIRECTORY NAME}) : contoso
      • ID d’application (client) ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444
    • Étendue configurée pour les données météorologiques provenant de MinimalApiJwt : ({API NAME}) : Weather.Get
    oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    

    L’exemple précédent concerne une application inscrite dans un locataire avec un type de locataire AAD B2C. Si l’application est inscrite dans un locataire ME-ID, l’URI d’ID d’application est différent et l’étendue est donc différente.

    Exemple :

    • URI d’ID d’application ({APP ID URI}) : api://{CLIENT ID} avec l’ID d’application (client) ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444
    • Étendue configurée pour les données météorologiques provenant de MinimalApiJwt : ({API NAME}) : Weather.Get
    oidcOptions.Scope.Add("api://00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    
  • Authority et ClientId : définit l’autorité et l’ID client pour les appels OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Exemple :

    • Autorité ({AUTHORITY}) : https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (utilise l’ID de locataire aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • ID client ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Exemple pour l’autorité « common » de Microsoft Azure :

    L’autorité « common » doit être utilisée pour les applications multilocataires. Vous pouvez également utiliser l’autorité « common » pour les applications monolocataires, mais un IssuerValidator personnalisé est nécessaire, comme indiqué plus loin dans cette section.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType : configure le gestionnaire OIDC pour effectuer seulement le flux du code d’autorisation. Les octrois implicites et les flux hybrides ne sont pas nécessaires dans ce mode.

    Dans la configuration de l’inscription d’application Octroi implicite et flux hybrides du portail Entra ou Azure, ne cochez pas la case pour que le point de terminaison d’autorisation retourne des Jetons d’accès ou des Jetons d’ID. Le gestionnaire OIDC demande automatiquement les jetons appropriés en utilisant le code retourné par le point de terminaison d’autorisation.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims et configuration de NameClaimType et de RoleClaimType : de nombreux serveurs OIDC utilisent « name » et « role » au lieu des valeurs par défaut de SOAP/WS-Fed dans ClaimTypes. Quand MapInboundClaims est défini sur false, le gestionnaire n’effectue pas de mappages de revendications et les noms de revendication du JWT sont utilisés directement par l’application. L’exemple suivant définit le type de revendication de rôle sur « roles », qui est approprié pour Microsoft Entra ID (ME-ID). Consultez la documentation de votre fournisseur d’identity pour plus d’informations.

    Remarque

    MapInboundClaims doit être défini sur false pour la plupart des fournisseurs OIDC, ce qui empêche le renommage des revendications.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuration du chemin d’accès : les chemins d’accès doivent correspondre à l’URI de redirection (chemin d’accès du rappel de la connexion) et publier les chemins d’accès de redirection de déconnexion (chemin d’accès du rappel déconnecté) configurés lors de l’inscription de l’application auprès du fournisseur OIDC. Dans le portail Azure, les chemins d’accès sont configurés dans le panneau Authentification de l’inscription de l’application. Les chemins d’accès de connexion et de déconnexion doivent être enregistrés en tant qu’URI de redirection. Les valeurs par défaut sont /signin-oidc et /signout-callback-oidc.

    • CallbackPath : le chemin d’accès de la demande dans le chemin d’accès de base de l’application où l’agent utilisateur est retourné.

      Dans le portail Entra ou Azure, définissez le chemin d’accès dans l’URI de redirection de la configuration de la plateforme web :

      https://localhost/signin-oidc

      Remarque

      Un port n’est pas nécessaire pour les adresses localhost.

    • SignedOutCallbackPath : chemin d’accès de requête dans le chemin d’accès de base de l’application où l’agent utilisateur est renvoyé après la déconnexion du fournisseur d’identity.

      Dans le portail Entra ou Azure, définissez le chemin d’accès dans l’URI de redirection de la configuration de la plateforme web :

      https://localhost/signout-callback-oidc

      Remarque

      Un port n’est pas nécessaire pour les adresses localhost.

      Remarque

      Si vous utilisez Microsoft Identity Web, le fournisseur redirige actuellement seulement vers SignedOutCallbackPath si l’autorité microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/) est utilisée. Cette limitation n’existe pas si vous pouvez utiliser l’autorité « common » avec Microsoft Identity Web. Pour plus d’informations, consultez postLogoutRedirectUri ne fonctionne pas quand l’URL de l’autorité contient un ID de locataire (AzureAD/microsoft-authentication-library-for-js #5783).

    • RemoteSignOutPath : les demandes reçues sur ce chemin d’accès font que le gestionnaire appelle la déconnexion en utilisant le schéma de déconnexion.

      Dans le portail Entra ou Azure, définissez l’URL de déconnexion du canal frontal :

      https://localhost/signout-oidc

      Remarque

      Un port n’est pas nécessaire pour les adresses localhost.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Exemples (valeurs par défaut) :

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure uniquement avec le point de terminaison « common ») TokenValidationParameters.IssuerValidator : de nombreux fournisseurs OIDC fonctionnent avec le validateur émetteur par défaut, mais nous devons tenir compte de l’émetteur paramétré avec l’ID de locataire ({TENANT ID}) retourné par https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Pour plus d’informations, consultez SecurityTokenInvalidIssuerException avec OpenID Connect et le point de terminaison « common » d’Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

    Uniquement pour les applications utilisant Microsoft Entra ID ou Azure AD B2C avec le point de terminaison « common » :

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Exemple de code d’application

Examinez les fonctionnalités suivantes dans l’exemple d’application :

  • Actualisation automatique des jetons non interactifs avec l’aide d’un actualiseur cookie personnalisé (CookieOidcRefresher.cs).
  • Le projet serveur appelle AddAuthenticationStateSerialization pour ajouter un fournisseur d’état d’authentification côté serveur qui utilise PersistentComponentState pour transmettre l’état d’authentification au client. Le client appelle AddAuthenticationStateDeserialization à désérialiser et à utiliser l’état d’authentification passé par le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.
  • Les requêtes adressées à l’Blazor Web App sont proxysées vers le projet d’API web back-end (MinimalApiJwt). MapForwarder dans le fichier Program ajoute le transfert direct des requêtes HTTP qui correspondent au modèle spécifié à une destination spécifique en utilisant la configuration par défaut pour la requête sortante, des transformations personnalisées et le client HTTP par défaut :
    • Lors du rendu du composant Weather sur le serveur, le composant utilise le ServerWeatherForecaster pour proxyser la demande de données météo avec le jeton d’accès de l’utilisateur.
    • Lorsque le composant est rendu sur le client, le composant utilise l’implémentation du service ClientWeatherForecaster, qui utilise un HttpClient préconfiguré (dans le fichier Program du projet client) pour effectuer un appel d’API web au projet de serveur. Un point de terminaison d’API minimal (/weather-forecast) défini dans le fichier Program du projet de serveur transforme la requête avec le jeton d’accès de l’utilisateur pour obtenir les données météo.
  • Actualisation automatique des jetons non interactifs avec l’aide d’un actualiseur cookie personnalisé (CookieOidcRefresher.cs).
  • La classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) est un AuthenticationStateProvider côté serveur qui utilise PersistentComponentState pour transmettre l’état de l’authentification au client, qui est ensuite fixé pour la durée de vie de l’application WebAssembly.
  • Les requêtes adressées à l’Blazor Web App sont proxysées vers le projet d’API web back-end (MinimalApiJwt). MapForwarder dans le fichier Program ajoute le transfert direct des requêtes HTTP qui correspondent au modèle spécifié à une destination spécifique en utilisant la configuration par défaut pour la requête sortante, des transformations personnalisées et le client HTTP par défaut :
    • Lors du rendu du composant Weather sur le serveur, le composant utilise le ServerWeatherForecaster pour proxyser la demande de données météo avec le jeton d’accès de l’utilisateur.
    • Lorsque le composant est rendu sur le client, le composant utilise l’implémentation du service ClientWeatherForecaster, qui utilise un HttpClient préconfiguré (dans le fichier Program du projet client) pour effectuer un appel d’API web au projet de serveur. Un point de terminaison d’API minimal (/weather-forecast) défini dans le fichier Program du projet de serveur transforme la requête avec le jeton d’accès de l’utilisateur pour obtenir les données météo.

Pour plus d’informations sur les appels d’API (web) à l’aide d’abstractions de service dans les Blazor Web App, consultez Appeler une API web à partir d’une application ASP.NET Core Blazor.

Projet d’Blazor Web App côté client (BlazorWebAppOidc.Client)

Le projet BlazorWebAppOidc.Client est le projet côté client de l’Blazor Web App.

Le client appelle AddAuthenticationStateDeserialization à désérialiser et à utiliser l’état d’authentification passé par le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.

La classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) est un AuthenticationStateProvider côté client qui détermine l’état d’authentification de l’utilisateur en recherchant les données conservées dans la page quand elle a été rendue sur le serveur. L’état d’authentification est fixe pour la durée de vie de l’application WebAssembly.

Si l’utilisateur doit se connecter ou se déconnecter, un rechargement complet de la page est nécessaire.

L’exemple d’application fournit un nom d’utilisateur et un e-mail seulement à des fins d’affichage. Il n’inclut pas de jetons qui s’authentifient auprès du serveur lors de requêtes ultérieures, celles-ci étant gérées séparément en utilisant un cookie qui est inclus dans les requêtes HttpClient envoyées au serveur.

Projet d’API web back-end (MinimalApiJwt)

Le projet MinimalApiJwt est une API web back-end pour plusieurs projets front-end. Le projet configure un point de terminaison d’API minimale pour les données météorologiques. Les requêtes provenant du projet côté serveur d’Blazor Web App (BlazorWebAppOidc) sont proxysées vers le projet MinimalApiJwt.

Configuration

Configurez le projet dans les JwtBearerOptions de l’appel AddJwtBearer dans le fichier Program du projet :

  • Audience : définit l’audience pour les jetons OpenID Connect reçus.

    Dans le portail Azure ou Entra : faites correspondre la valeur au chemin d’accès de l’URI d’ID d’application configuré lors de l’ajout de l’étendue Weather.Get sous Exposer une API :

    jwtOptions.Audience = "{APP ID URI}";
    

    Exemple :

    URI ID d’application : ({APP ID URI}) : https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}

    • Nom du répertoire : ({DIRECTORY NAME}) : contoso
    • ID d’application (client) ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444
    jwtOptions.Audience = "https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444";
    

    L’exemple précédent concerne une application inscrite dans un locataire avec un type de locataire AAD B2C. Si l’application est inscrite dans un locataire ME-ID, l’URI d’ID d’application est différent et l’étendue est donc différente.

    Exemple :

    URI d’ID d’application ({APP ID URI}) : api://{CLIENT ID} avec l’ID d’application (client) ({CLIENT ID}) : 00001111-aaaa-2222-bbbb-3333cccc4444

    jwtOptions.Audience = "api://00001111-aaaa-2222-bbbb-3333cccc4444";
    
  • Authority : définit l’autorité pour effectuer des appels OpenID Connect. Faire correspondre la valeur à l’autorité configurée pour le gestionnaire OIDC dans BlazorWebAppOidc/Program.cs :

    jwtOptions.Authority = "{AUTHORITY}";
    

    Exemple :

    Autorité ({AUTHORITY}) : https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (utilise l’ID de locataire aaaabbbb-0000-cccc-1111-dddd2222eeee)

    jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    

    L’exemple précédent concerne une application inscrite dans un locataire avec un type de locataire AAD B2C. Si l’application est inscrite dans un locataire ME-ID, l’autorité doit correspondre à l’émetteur (iss) du jeton JWT renvoyé par le fournisseur d’identity :

    jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
    

API minimale pour les données météorologiques

Sécurisez le point de terminaison des données de prévision météorologique dans le fichier Program du projet :

app.MapGet("/weather-forecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
}).RequireAuthorization();

La méthode d’extension RequireAuthorization nécessite une autorisation pour la définition de route. Pour les contrôleurs que vous ajoutez au projet, ajoutez l’attribut [Authorize] au contrôleur ou à l’action.

Ajout de composants qui adoptent le rendu interactif côté serveur

Étant donné que l’application utilise le rendu automatique interactif global via le composant Routes, des composants individuels qui spécifient le rendu interactif côté serveur (SSR interactif, @rendermode InteractiveServer) dans leur fichier de définition de composant (.razor) sont placés dans le projet .Client se trouvant dans le dossier Pages.

Le placement de composants SSR interactifs dans le projet .Client est contre-intuitif, car ces composants ne sont rendus que sur le serveur.

Si vous placez un composant SSR interactif dans le dossier Components/Pages du projet de serveur, le composant est présenté normalement et brièvement affiché dans le navigateur de l’utilisateur. Toutefois, le routeur côté client n’est pas en mesure de trouver le composant, ce qui aboutit à un 404 - Introuvable dans le navigateur.

Par conséquent, placez des composants SSR interactifs du projet .Client se trouvant dans le dossier Pages.

Rediriger vers la page home lors de la déconnexion

Quand un utilisateur navigue dans l’application, le composant LogInOrOut (Layout/LogInOrOut.razor) définit un champ masqué pour l’URL de retour (ReturnUrl) sur la valeur de l’URL actuelle (currentURL). Quand l’utilisateur se déconnecte de l’application, le fournisseur d’identity le renvoie vers la page à partir de laquelle il s’est déconnecté.

Si l’utilisateur se déconnecte d’une page sécurisée, il est renvoyé à la même page sécurisée après la déconnexion seulement pour être renvoyé via le processus d’authentification. Ce comportement convient quand les utilisateurs doivent changer de compte fréquemment. Cependant, une autre spécification d’application peut demander à ce que l’utilisateur soit renvoyé vers la page home de l’application ou vers une autre page après la déconnexion. L’exemple suivant montre comment définir la page home de l’application comme URL de renvoi pour les opérations de déconnexion.

Les modifications importantes apportées au composant LogInOrOut sont illustrées dans l’exemple suivant. Il n’est pas nécessaire de fournir un champ masqué pour l’ensemble ReturnUrl de la home page, / car il s’agit du chemin d’accès par défaut. IDisposable n’est plus implémenté. Le NavigationManager n’est plus injecté. Le bloc @code tout entier est supprimé.

Layout/LogInOrOut.razor:

@using Microsoft.AspNetCore.Authorization

<div class="nav-item px-3">
    <AuthorizeView>
        <Authorized>
            <form action="authentication/logout" method="post">
                <AntiforgeryToken />
                <button type="submit" class="nav-link">
                    <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
                    </span> Logout @context.User.Identity?.Name
                </button>
            </form>
        </Authorized>
        <NotAuthorized>
            <a class="nav-link" href="authentication/login">
                <span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> 
                Login
            </a>
        </NotAuthorized>
    </AuthorizeView>
</div>

Nonce de chiffrement

Un nonce est une chaîne de caractères qui associe la session d’un client à un jeton d’identification afin d’atténuer les attaques par relecture.

Si vous recevez une erreur de nonce pendant le développement et les tests d’authentification, utilisez une nouvelle session de navigateur InPrivate/incognito pour chaque exécution de test, quelle que soit l’importance du changement apporté à l’application ou à l’utilisateur de test, car des données cookie obsolètes peuvent entraîner une erreur de nonce. Pour plus d’informations, consultez la section Cookies et données de site.

Il n’est pas nécessaire d’utiliser un nonce lorsqu’un jeton d’actualisation est échangé contre un nouveau jeton d’accès. Dans l’exemple d’application, le CookieOidcRefresher (CookieOidcRefresher.cs) définit délibérément OpenIdConnectProtocolValidator.RequireNonce sur false.

Rôles d’application pour les applications non inscrites auprès de Microsoft Entra (ME-ID)

Cette section concerne les applications qui n’utilisent pas Microsoft Entra ID (ME-ID) comme fournisseur d’identity. Pour les applications inscrites auprès de ME-ID, consultez la section Rôles d’application pour les applications inscrites auprès de Microsoft Entra (ME-ID).

Configurez le type de revendication de rôle (TokenValidationParameters.RoleClaimType) dans les OpenIdConnectOptions de Program.cs :

oidcOptions.TokenValidationParameters.RoleClaimType = "{ROLE CLAIM TYPE}";

Pour de nombreux fournisseurs d’identity OIDC, le type de revendication de rôle est role. Consultez la documentation de votre fournisseur d’identity pour connaître la valeur correcte.

Remplacez la classe UserInfo dans le projet BlazorWebAppOidc.Client par la classe suivante.

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "role";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

À ce stade, les composants Razor peuvent adopter une autorisation basée sur des rôles et basée sur des stratégies. Les rôles d’application apparaissent dans les revendications role, une revendication par rôle.

Rôles d’application pour les applications inscrites auprès de Microsoft Entra (ME-ID)

Utilisez les instructions de cette section pour implémenter des rôles d’application, des groupes de sécurité ME-ID et des rôles d’administrateur ME-ID intégrés pour les applications à l’aide de Microsoft Entra ID (ME-ID).

L’approche décrite dans cette section configure ME-ID pour envoyer des groupes et des rôles dans l’en-tête cookie d’authentification. Lorsque les utilisateurs ne sont membres que de quelques groupes et rôles de sécurité, l’approche suivante doit fonctionner pour la plupart des plateformes d’hébergement sans rencontrer de problème où les en-têtes sont trop longs, par exemple avec l’hébergement IIS qui a une limite de longueur d’en-tête par défaut de 16 Ko (MaxRequestBytes). Si la longueur d’en-tête est un problème en raison de l’appartenance à un groupe ou à un rôle élevé, nous vous recommandons de ne pas suivre les instructions de cette section en faveur de l’implémentation de Microsoft Graph pour obtenir séparément les groupes et rôles d’un utilisateur à partir de ME-ID, une approche qui n’augmente pas la taille de l’authentification cookie. Pour plus d’informations, consultez Requête incorrecte - Requête trop longue - Serveur IIS (dotnet/aspnetcore #57545).

Configurez le type de revendication de rôle (TokenValidationParameters.RoleClaimType) dans OpenIdConnectOptions de Program.cs. Définissez la valeur sur roles :

oidcOptions.TokenValidationParameters.RoleClaimType = "roles";

Bien que vous ne puissiez pas attribuer de rôles à des groupes sans compte ME-ID Premium, vous pouvez attribuer des rôles à des utilisateurs et recevoir des revendications de rôle pour des utilisateurs avec un compte Azure standard. Les conseils de cette section ne nécessitent pas de compte ME-ID Premium.

Lorsque vous utilisez le répertoire par défaut, suivez les instructions détaillées dans Ajouter des rôles d’application dans votre application et les recevoir dans le jeton (documentation ME-ID) pour configurer et attribuer des rôles. Si vous n’utilisez pas le répertoire par défaut, modifiez le manifeste de l’application dans le portail Azure pour établir manuellement les rôles de l’application dans l’entrée appRoles du fichier manifeste. Pour plus d’informations, consultez Configurer la revendication de rôle (documentation ME-ID).

Les groupes de sécurité Azure d’un utilisateur arrivent dans des revendications groups, et les attributions de rôle d’administrateur ME-ID intégrées d’un utilisateur arrivent dans des revendications d’ID connus (wids). Les valeurs des deux types de revendications sont des GUID. Lorsqu’elles sont reçues par l’application, ces revendications peuvent être utilisées pour établir une autorisation de rôle et de stratégie dans les composants Razor.

Dans le manifeste de l’application dans le portail Azure, définissez l’attribut groupMembershipClaims sur All. Une valeur de All entraîne l’envoi par ME-ID de tous les groupes de sécurité/distribution (revendications groups) et rôles (revendications wids) de l’utilisateur connecté. Pour définir l’attribut groupMembershipClaims :

  1. Ouvrez l’inscription de l’application dans le portail Azure.
  2. Sélectionnez Gérer>Manifeste dans la barre latérale.
  3. Recherchez l’attribut groupMembershipClaims.
  4. Définissez la valeur sur All ("groupMembershipClaims": "All").
  5. Sélectionnez le bouton Enregistrer.

Remplacez la classe UserInfo dans le projet BlazorWebAppOidc.Client par la classe suivante.

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }
    public required string[] Groups { get; init; }
    public required string[] Wids { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "roles";
    private const string GroupsClaimType = "groups";
    private const string WidsClaimType = "wids";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
            Groups = principal.FindAll(GroupsClaimType).Select(c => c.Value)
                .ToArray(),
            Wids = principal.FindAll(WidsClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat(Groups.Select(role => new Claim(GroupsClaimType, role)))
                .Concat(Wids.Select(role => new Claim(WidsClaimType, role)))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

À ce stade, les composants Razor peuvent adopter une autorisation basée sur des rôles et basée sur des stratégies :

  • Les rôles d’application apparaissent dans les revendications roles, une revendication par rôle.
  • Les groupes de sécurité apparaissent dans les revendications groups, une revendication par groupe. Les GUID des groupes de sécurité apparaissent dans le portail Azure lorsque vous créez un groupe de sécurité et sont répertoriés lors de la sélection de Identity>Vue d’ensemble>Groupes>Affichage.
  • Les rôles d’administrateur ME-ID intégrés apparaissent dans les revendications wids, une revendication par rôle. La revendication wids avec la valeur b79fbf4d-3ef9-4689-8143-76b194e85509 est toujours envoyée par ME-ID pour les comptes non invités du locataire et ne fait pas référence à un rôle d’administrateur. Les GUID de rôle d’administrateur (ID de modèle de rôle) apparaissent dans le portail Azure lors de la sélection de Rôles et administrateurs, suivis des points de suspension (...) >Description du rôle répertorié. Les ID de modèle de rôle sont également répertoriés dans les rôles intégrés Microsoft Entra (documentation Entra).

Résolution des problèmes

Logging

L’application serveur est une application standard ASP.NET Core. Consultez les instructions de journalisation ASP.NET Core pour activer un niveau de journalisation inférieur dans l’application serveur.

Pour activer la journalisation de débogage ou de trace pour l’authentification Blazor WebAssembly, consultez la section Journalisation d’authentification côté client de la Journalisation Blazor ASP.NET Core avec le sélecteur de version d’article défini sur ASP.NET Core 7.0 ou une version ultérieure.

Erreurs courantes

  • Mauvaise configuration de l’application ou du fournisseur d’identité (Identity Provider)

    Les erreurs les plus courantes sont provoquées par une configuration incorrecte. Voici quelques exemples :

    • Selon les besoins du scénario, une autorité, une instance, un ID de locataire, un domaine de locataire, un ID client ou un URI de redirection manquant ou incorrect empêche une application d’authentifier les clients.
    • Les étendues de requêtes erronées empêchent les clients d’accéder aux points de terminaison d’API web du serveur.
    • Des autorisations d’API serveur incorrectes ou manquantes empêchent les clients d’accéder aux points de terminaison d’API web du serveur.
    • Exécution de l’application sur un autre port que celui configuré dans l’URI de redirection de l’inscription d’application de l’IP. Notez qu’un port n’est pas requis pour Microsoft Entra ID et pour une application s’exécutant à une adresse de test de développement localhost. Cependant, la configuration du port de l’application et le port où l’application s’exécute doivent correspondre pour les adresses non-localhost.

    La couverture de la configuration dans cet article présente des exemples de la configuration correcte. Vérifiez soigneusement la configuration à la recherche d’une application et d’une configuration incorrecte de l’adresse IP.

    Si la configuration semble correcte :

    • Analysez les journaux des applications.

    • Examinez le trafic réseau entre l’application cliente et le fournisseur d’identité ou l’application serveur à l’aide des outils de développement du navigateur. Bien souvent, un message d’erreur exact ou un message indiquant la cause du problème est retourné au client par le fournisseur d’identité ou l’application serveur, une fois qu’une demande a été effectuée. Vous trouverez des conseils d’aide sur les outils de développement dans les articles suivants :

    L’équipe de documentation peut répondre aux commentaires et bogues relatifs aux articles (ouvrez un problème à partir de la section de commentaires de cette page). Toutefois, elle ne peut pas fournir de support produit. Plusieurs forums de support publics sont disponibles pour vous aider à résoudre les problèmes liés à une application. Nous recommandons ce qui suit :

    Les forums précédents ne sont pas détenus ou contrôlés par Microsoft.

    Pour les rapports de bogues de framework reproductibles, non liés à la sécurité, non sensibles et non confidentiels, ouvrez un problème auprès de l’unité de produit ASP.NET Core. N’ouvrez pas de problème auprès de l’unité de produit tant que vous n’avez pas investigué de manière approfondie la cause du problème, sans pouvoir le résoudre par vous-même ou avec l’aide de la communauté sur un forum de support public. L’unité de produit ne peut pas résoudre les problèmes d’applications individuelles qui sont défaillantes en raison d’une mauvaise configuration ou de cas d’usage impliquant des services tiers. Si un rapport est de nature sensible ou confidentielle, ou s’il décrit une faille de sécurité potentielle dans le produit que des attaquants peuvent exploiter, consultez Signaler des problèmes de sécurité et des bugs (référentiel GitHub dotnet/aspnetcore).

  • Client non autorisé pour ME-ID

    Info : Échec de l’autorisation Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]. Ces conditions n’ont pas été remplies : DenyAnonymousAuthorizationRequirement : Nécessite un utilisateur authentifié.

    Erreur de rappel de la connexion à partir de ME-ID :

    • Erreur : unauthorized_client
    • Description : AADB2C90058: The provided application is not configured to allow public clients.

    Pour résoudre l’erreur :

    1. Dans le portail Azure, accédez au manifeste de l’application.
    2. Affectez à l’attribut allowPublicClient la valeur null ou true.

Cookies et données de site

Les cookies et les données de site peuvent persister au fil des mises à jour des applications, et interférer avec les tests et la résolution des problèmes. Effacez ce qui suit quand vous apportez des changements au code d’application, au compte d’utilisateur du fournisseur ou à la configuration de l’application :

  • Cookies de connexion des utilisateurs
  • Cookies d’applications
  • Données de site mises en cache et stockées

Il existe une approche qui permet d’empêcher les cookies et les données de site persistants d’interférer avec les tests et la résolution des problèmes. Elle consiste à :

  • Configurer un navigateur
    • Utilisez un navigateur de test que vous pouvez configurer pour supprimer tous les cookies et toutes les données de site à chaque fois qu’il se ferme.
    • Vérifiez que le navigateur est fermé manuellement ou par l’IDE chaque fois qu’un changement est apporté à la configuration de l’application, de l’utilisateur de test ou du fournisseur.
  • Utilisez une commande personnalisée pour ouvrir un navigateur en mode InPrivate ou Incognito dans Visual Studio :
    • Ouvrez la boîte de dialogue Parcourir avec à partir du bouton Exécuter de Visual Studio.
    • Cliquez sur le bouton Ajouter.
    • Indiquez le chemin de votre navigateur dans le champ Programme. Les chemins d’exécutables suivants sont des emplacements d’installation classiques de Windows 10. Si votre navigateur est installé à un autre emplacement, ou si vous n’utilisez pas Windows 10, indiquez le chemin de l’exécutable du navigateur.
      • Microsoft Edge : C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome : C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox : C:\Program Files\Mozilla Firefox\firefox.exe
    • Dans le champ Arguments, indiquez l’option de ligne de commande utilisée par le navigateur pour qu’il s’ouvre en mode InPrivate ou Incognito. Certains navigateurs nécessitent l’URL de l’application.
      • Microsoft Edge : Utilisez -inprivate.
      • Google Chrome : utilisez --incognito --new-window {URL}, où l’espace réservé {URL} correspond à l’URL à ouvrir (par exemple, https://localhost:5001).
      • Mozilla Firefox : utilisez -private -url {URL}, où l’espace réservé {URL} correspond à l’URL à ouvrir (par exemple https://localhost:5001).
    • Indiquez un nom dans le champ Nom convivial. Par exemple, Firefox Auth Testing
    • Cliquez sur le bouton OK.
    • Pour éviter d’avoir à sélectionner le profil de navigateur pour chaque itération de test avec une application, définissez le profil en tant que profil par défaut avec le bouton Par défaut.
    • Vérifiez que le navigateur est fermé par l’IDE chaque fois qu’un changement est apporté à la configuration de l’application, de l’utilisateur de test ou du fournisseur.

Mises à niveau d’application

Une application fonctionnelle peut échouer immédiatement après la mise à niveau du kit SDK .NET Core sur l’ordinateur de développement ou la modification des versions de package au sein de l’application. Dans certains cas, les packages incohérents peuvent bloquer une application quand vous effectuez des mises à niveau majeures. Vous pouvez résoudre la plupart de ces problèmes en suivant les instructions suivantes :

  1. Effacez les caches de package NuGet du système local en exécutant dotnet nuget locals all --clear à partir d’un interpréteur de commandes.
  2. Supprimez les dossiers bin et obj du projet.
  3. Restaurez et regénérez le projet.
  4. Supprimez tous les fichiers du dossier de déploiement sur le serveur avant de redéployer l’application.

Remarque

L’utilisation de versions de package incompatibles avec le framework cible de l’application n’est pas prise en charge. Pour plus d’informations sur un package, utilisez la Galerie NuGet ou l’Explorateur de packages FuGet.

Exécuter l’application serveur

Lors du test et de la résolution des problèmes d’une Blazor Web App, vérifiez que vous exécutez l’application à partir du projet de serveur.

Inspecter l’utilisateur

Le composant UserClaims suivant peut être utilisé directement dans les applications, ou servir de base à une personnalisation supplémentaire.

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li><b>@claim.Type:</b> @claim.Value</li>
        }
    </ul>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    [CascadingParameter]
    private Task<AuthenticationState>? AuthState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthState == null)
        {
            return;
        }

        var authState = await AuthState;
        claims = authState.User.Claims;
    }
}

Ressources supplémentaires