Partager via


Sécuriser un ASP.NET Core Blazor Web App avec l’ID Microsoft Entra

Cet article explique comment sécuriser un package Web Microsoft plateforme/Microsoft Identity identity pour Microsoft Entra ID à l’aide d’un exemple d’application. Blazor Web App

La spécification suivante est couverte :

  • Utilise Blazor Web App le mode de rendu automatique avec l’interactivité globale (InteractiveAuto).
  • 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.
  • L’application utilise l’ID Microsoft Entra, basé sur les packages Web MicrosoftIdentity.
  • L’actualisation automatique des jetons non interactifs est gérée par l’infrastructure.
  • L’application utilise des abstractions de service côté serveur et côté client pour afficher les données météorologiques générées :
    • Lors du rendu du Weather composant sur le serveur pour afficher les données météorologiques, le composant utilise le ServerWeatherForecaster serveur pour obtenir directement des données météorologiques (et non via un appel d’API web).
    • Lorsque le Weather composant est rendu sur le client, le composant utilise l’implémentation ClientWeatherForecaster du service, qui utilise un fichier préconfiguré HttpClient (dans le fichier du Program projet client) pour effectuer un appel d’API web à l’API minimale (/weather-forecast) du projet serveur pour les données météorologiques. Le point de terminaison d’API minimal obtient les données météorologiques de la ServerWeatherForecaster classe et les retourne au client pour le rendu par le composant.

Exemple d’application

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

  • BlazorWebAppEntra : 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.
  • BlazorWebAppEntra.Client : projet côté client de l’Blazor Web App.

Accédez à des exemples d’applications via le 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 BlazorWebAppEntra dossier pour .NET 9 ou version ultérieure.

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

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

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

Le fichier BlazorWebAppEntra.http peut être utilisé pour tester la requête de données météorologiques. Notez que le projet BlazorWebAppEntra 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.

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

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

Si l’utilisateur doit se connecter ou sortir pendant le rendu côté client, un rechargement de page complet est lancé.

Configuration

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

AddMicrosoftIdentityWebApp à partir de Microsoft Identity Web (Microsoft.Identity.Web package NuGet, documentation de l’API) est configuré par la AzureAd section du fichier du appsettings.json projet de serveur.

Dans l’inscription de l’application dans l’Entra ou Portail Azure, utilisez une configuration de plateforme web avec un URI de redirection (https://localhost/signin-oidcun port n’est pas obligatoire). Vérifiez que les jetons d’ID et les jetons d’accès sous octroi implicite et les flux hybrides ne sont pas sélectionnés. Le gestionnaire OpenID Connect demande automatiquement les jetons appropriés à l’aide du code retourné par le point de terminaison d’autorisation.

Configurer l’application

Dans le fichier de paramètres d’application du projet de serveur (appsettings.json), fournissez la configuration de section de AzureAd l’application. Obtenez l’ID d’application (client), le domaine de locataire (éditeur) et l’ID d’annuaire (locataire) de l’application à partir de l’inscription de l’application dans l’Entra ou Portail Azure :

"AzureAd": {
  "CallbackPath": "/signin-oidc",
  "ClientId": "{CLIENT ID}",
  "Domain": "{DOMAIN}",
  "Instance": "https://login.microsoftonline.com/",
  "ResponseType": "code",
  "TenantId": "{TENANT ID}"
},

Espaces réservés dans l’exemple précédent :

  • {CLIENT ID}: ID d’application (client).
  • {DOMAIN}: domaine du locataire (éditeur).
  • {TENANT ID}: ID d’annuaire (locataire).

Exemple :

"AzureAd": {
  "CallbackPath": "/signin-oidc",
  "ClientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "Domain": "contoso.onmicrosoft.com",
  "Instance": "https://login.microsoftonline.com/",
  "ResponseType": "code",
  "TenantId": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
},

Le chemin de rappel (CallbackPath) doit correspondre à l’URI de redirection (chemin de rappel de connexion) configuré lors de l’inscription de l’application dans l’Entra ou Portail Azure. Les chemins d’accès sont configurés dans le panneau Authentification de l’inscription de l’application. La valeur CallbackPath par défaut est /signin-oidc pour un URI de redirection inscrit ( https://localhost/signin-oidc un port n’est pas obligatoire).

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.

Établir la clé secrète client

Créez une clé secrète client dans l’inscription d’ID Entra 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.

Utilisez l’une ou l’autre des approches suivantes pour fournir la clé secrète client à l’application :

  • Outil Secret Manager : l’outil Secret Manager stocke les données privées sur l’ordinateur local et est utilisé uniquement pendant le développement local.
  • Azure Key Vault : vous pouvez stocker la clé secrète client dans un coffre de clés à utiliser dans n’importe quel environnement, y compris pour l’environnement de développement lors de l’utilisation locale. Certains développeurs préfèrent utiliser des coffres de clés pour les déploiements intermédiaires et de production et utiliser l’outil Secret Manager pour le développement local.

Nous vous recommandons vivement d’éviter de stocker les secrets client dans le code du projet ou les fichiers de configuration. Utilisez des flux d’authentification sécurisés, tels que l’une ou l’autre des approches de cette section.

Outil Gestionnaire de secrets

L’outil Gestionnaire de secrets peut stocker la clé secrète client de l’application serveur sous la clé AzureAd:ClientSecretde configuration.

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, qui est utilisé en interne par l’outil pour suivre les secrets de l’application :

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 Entra de l’application :

dotnet user-secrets set "AzureAd:ClientSecret" "{SECRET}"

Si vous utilisez Visual Studio, vous pouvez vérifier 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.

Azure Key Vault

Azure Key Vault offre une approche sécurisée pour fournir la clé secrète client de l’application à l’application.

Pour créer un coffre de clés et définir une clé secrète client, consultez À propos des secrets Azure Key Vault (documentation Azure), qui relient les ressources pour commencer à utiliser Azure Key Vault. Pour implémenter le code de cette section, enregistrez l’URI du coffre de clés et le nom du secret à partir d’Azure lorsque vous créez le coffre de clés et le secret. Lorsque vous définissez la stratégie d’accès pour le secret dans le volet Stratégies d’accès :

  • Seule l’autorisation Obtenir le secret est requise.
  • Sélectionnez l’application comme principal du secret.

Important

Un secret key vault est créé avec une date d’expiration. Veillez à suivre l’expiration d’un secret key vault et à créer un secret pour l’application avant cette date.

La méthode suivante GetKeyVaultSecret récupère un secret à partir d’un coffre de clés. Ajoutez cette méthode au projet serveur. Ajustez l’espace de noms (BlazorSample.Helpers) pour qu’il corresponde au schéma d’espace de noms de votre projet.

Helpers/AzureHelper.cs:

using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace BlazorSample.Helpers;

public static class AzureHelper
{
    public static string GetKeyVaultSecret(string tenantId, string vaultUri, string secretName)
    {
        DefaultAzureCredentialOptions options = new()
        {
            // Specify the tenant ID to use the dev credentials when running the app locally
            // in Visual Studio.
            VisualStudioTenantId = tenantId,
            SharedTokenCacheTenantId = tenantId
        };

        var client = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential(options));
        var secret = client.GetSecretAsync(secretName).Result;

        return secret.Value.Value;
    }
}

Où les services sont inscrits dans le fichier du Program projet de serveur, obtenez et appliquez la clé secrète client à l’aide du code suivant :

var tenantId = builder.Configuration.GetValue<string>("AzureAd:TenantId")!;
var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;
var secretName = builder.Configuration.GetValue<string>("AzureAd:SecretName")!;

builder.Services.Configure<MicrosoftIdentityOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
    {
        options.ClientSecret = 
            AzureHelper.GetKeyVaultSecret(tenantId, vaultUri, secretName);
    });

Si vous souhaitez contrôler l’environnement dans lequel le code précédent fonctionne, par exemple pour éviter d’exécuter le code localement, car vous avez choisi d’utiliser l’outil Gestionnaire de secrets pour le développement local, vous pouvez encapsuler le code précédent dans une instruction conditionnelle qui vérifie l’environnement :

if (!context.HostingEnvironment.IsDevelopment())
{
    ...
}

Dans la AzureAd section de appsettings.json, ajoutez les clés et SecretName valeurs de configuration suivantes VaultUri :

"VaultUri": "{VAULT URI}",
"SecretName": "{SECRET NAME}"

Dans l'exemple précédent :

  • L’espace {VAULT URI} réservé est l’URI du coffre de clés. Incluez la barre oblique de fin sur l’URI.
  • L’espace {SECRET NAME} réservé est le nom du secret.

Exemple :

"VaultUri": "https://contoso.vault.azure.net/",
"SecretName": "BlazorWebAppEntra"

La configuration est utilisée pour faciliter l’approvisionnement de coffres de clés dédiés et de noms de secrets en fonction des fichiers de configuration environnementaux de l’application. Par exemple, vous pouvez fournir différentes valeurs de configuration pour appsettings.Development.json le développement, appsettings.Staging.json lors de la préproduction et appsettings.Production.json pour le déploiement de production. Pour plus d’informations, consultez Configuration d’ASP.NET Core Blazor.

Rediriger vers la page lors de la home 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. Toutefois, une autre spécification d’application peut appeler l’utilisateur à retourner à la page de home l’application ou à une autre page après la déconnexion. L’exemple suivant montre comment définir la page de l’application home comme URL de retour 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>

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