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.

Établir la clé secrète client

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

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.

Exécutez la commande suivante dans un interpréteur de commandes à partir du répertoire du projet de serveur, par exemple l’interpréteur de commandes Developer PowerShell dans Visual Studio. L’espace {SECRET} réservé est la clé secrète client obtenue à partir de l’inscription de l’application :

dotnet user-secrets set "AzureAd: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

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.

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