Partager via


Utiliser Graph API avec ASP.NET Core Blazor WebAssembly

Note

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.

Warning

Cette version d'ASP.NET Core n'est plus prise en charge. Pour plus d’informations, consultez la stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.

Cet article explique comment utiliser Microsoft Graph dans les applications Blazor WebAssembly, ce qui permet aux applications d'accéder aux ressources Microsoft Cloud.

Deux approches sont couvertes :

  • Graph SDK : Le Microsoft Graph SDK simplifie la création d'applications de haute qualité, efficaces et résilientes qui accèdent à Microsoft Graph. Sélectionnez le bouton Graph SDK en haut de cette rubrique pour adopter cette approche.

  • HttpClient nommé avec Graph API : Un HttpClient nomméHttpClient peut envoyer des requêtes Microsoft Graph API directement à Microsoft Graph. Sélectionnez le bouton HttpClient nommé avec Graph API en haut de cette rubrique pour adopter cette approche.

Les conseils fournis dans cet article ne remplacent pas la documentation Microsoft Graph ni les conseils de sécurité Azure disponibles dans les autres documentations de Microsoft. Évaluez les conseils de sécurité dans la section Ressources supplémentaires de cet article avant de mettre en œuvre Microsoft Graph dans un environnement de production. Suivez les bonnes pratiques de Microsoft pour limiter les vulnérabilités de vos applications.

Des approches additionnelles pour travailler avec Microsoft Graph et Blazor WebAssembly sont fournies par les exemples Microsoft Graph et Azure suivants :

Pour envoyer votre feedback sur l'un ou l'autre des deux exemples ci-dessus, ouvrez un ticket sur le référentiel GitHub de l'exemple. Si vous ouvrez un ticket pour l'exemple Azure, fournissez un lien vers l'exemple dans votre commentaire initial car le référentiel d'exemples Azure (Azure-Samples) contient de nombreux exemples. Décrivez le problème en détail et incluez un échantillon de code si nécessaire. Déposez une application minimale sur GitHub qui reproduit le problème ou l’erreur. Assurez-vous de supprimer les données de configuration du compte Azure de l'exemple avant de le valider dans le référentiel public.

Pour envoyer des commentaires ou obtenir de l’aide concernant cette rubrique ou ASP.NET Core, veuillez consulter la section Principes de base d’ASP.NET Core Blazor.

Important

Les scénarios décrits dans cet article s’appliquent à l’utilisation de Microsoft Entra (ME-ID) comme fournisseur d’identité, et non AAD B2C. L’utilisation de Microsoft Graph avec une application Blazor WebAssembly côté client et le fournisseur d’identité AAD B2C n’est pas pris en charge pour l’instant, car l’application nécessite une clé secrète client, qui ne peut pas être sécurisée dans l’application Blazor côté client. Pour qu'une application Blazor WebAssembly autonome AAD B2C utilise Graph API, créez une API serveur (web) backend pour accéder à Graph API au nom des utilisateurs. L'application côté client authentifie et autorise les utilisateurs à appeler l'API web afin d'accéder de manière sécurisée à Microsoft Graph et de renvoyer les données à l’application Blazor côté client depuis votre API web basée sur le serveur. Le secret client est conservé en toute sécurité dans l'API web basée sur le serveur, et non dans l'application Blazor côté client. Ne stockez jamais un secret client dans une application Blazor côté client.

L'utilisation d'une application Blazor WebAssembly hébergée est prise en charge, lorsque l'application Server utilise le kit SDK/API Graph pour fournir les données Graph à l'application Client via une API web. Pour en savoir plus, veuillez consulter la section Solutions Blazor WebAssembly WebAssembly hébergées de cet article.

Les exemples de cet article tirent parti des nouvelles fonctionnalités de .NET/C#. Lorsque vous utilisez les exemples avec .NET 7 ou une version antérieure, quelques modifications mineures sont nécessaires. Cependant, le texte et les exemples de code concernant l’interaction avec Microsoft Graph sont identiques pour toutes les versions d’ASP.NET Core.

Les conseils suivants s'appliquent à Microsoft Graph v5.

Le kit Microsoft Graph SDK destiné aux applications Blazor s'appelle la bibliothèque cliente Microsoft Graph .NET.

Les exemples Graph SDK nécessitent les références de package suivantes dans l’application autonome Blazor WebAssembly. Les deux premiers packages sont déjà référencés si l'application a été activée pour l'authentification MSAL, par exemple lors de la création de l'application en suivant les conseils de la rubrique Sécuriser une application Blazor WebAssembly autonome ASP.NET Core avec Microsoft Entra ID.

Les exemples Graph SDK nécessitent les références de package suivantes dans l’application autonome Blazor WebAssembly ou l’application Client d’une solution hébergée Blazor WebAssembly. Les deux premiers packages sont déjà référencés si l'application a été activée pour l'authentification MSAL, par exemple lors de la création de l'application en suivant les conseils de la rubrique Sécuriser une application Blazor WebAssembly autonome ASP.NET Core avec Microsoft Entra ID.

Note

Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.

Dans le portail Azure, accordez des autorisations déléguées (étendues) pour les données Microsoft Graph auxquelles l'application doit pouvoir accéder au nom d'un utilisateur. Pour l'exemple de cette rubrique, l’enregistrement de l’application doit inclure l’autorisation déléguée permettant de lire les données utilisateur (étendue Microsoft.Graph>User.Read dans Autorisations d’API, Type : Déléguée). L’étendue User.Read permet aux utilisateurs de se connecter à l’application et permet à l’application de lire le profil et les informations de l’entreprise des utilisateurs connectés. Pour plus d’informations, consultez Vue d’ensemble des autorisations et du consentement dans la plateforme d’identités Microsoft et Vue d’ensemble des autorisations Microsoft Graph.

Permissions et étendues signifient la même chose et sont interchangeables dans la documentation de sécurité et dans le Portail Azure. À moins que le texte ne fasse référence au portail Azure, cette rubrique utilise étendue/étendues pour désigner les autorisations Graph.

Les étendues ne sont pas sensibles à la casse, donc User.Read est équivalent à user.read. Vous pouvez utiliser l’un ou l’autre format, mais nous vous recommandons de faire un choix cohérent dans l’ensemble du code de l’application.

Après avoir ajouté les étendues Microsoft Graph API à l’enregistrement de l’application dans le portail Azure, ajoutez la configuration des paramètres de l’application suivante dans le fichier wwwroot/appsettings.json de l’application, qui inclut l’URL de base Graph avec la version de Microsoft Graph et les étendues. Dans l’exemple suivant, l’étendue User.Read est spécifiée pour les exemples présentés dans les sections suivantes de cette rubrique. Les étendues ne sont pas sensibles à la casse.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com",
  "Version": "{VERSION}",
  "Scopes": [
    "user.read"
  ]
}

Dans l’exemple précédent, l’espace réservé {VERSION} correspond à la version de l’API Microsoft Graph (par exemple : v1.0).

Voici un exemple de fichier de configuration complet wwwroot/appsettings.json pour une application qui utilise ME-ID comme fournisseur d’identité, où la lecture des données utilisateur (étendueuser.read) est spécifiée pour Microsoft Graph :

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com",
    "Version": "v1.0",
    "Scopes": [
      "user.read"
    ]
  }
}

Dans l’exemple précédent, l’espace réservé {TENANT ID} correspond à l’ID de répertoire (locataire), et l’espace réservé {CLIENT ID} correspond à l’ID d’application (client). Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Ajoutez la classe GraphClientExtensions suivante à l'application autonome. Les étendues sont fournies à la propriété Scopes du AccessTokenRequestOptions dans la méthode AuthenticateRequestAsync.

Ajoutez la classe GraphClientExtensions suivante à l'application autonome ou à l'application Client d'une Blazor WebAssemblysolution hébergée. Les étendues sont fournies à la propriété Scopes du AccessTokenRequestOptions dans la méthode AuthenticateRequestAsync.

Lorsqu'un jeton d'accès n'est pas obtenu, le code suivant ne définit pas d'en-tête d'autorisation Bearer pour les requêtes Graph.

GraphClientExtensions.cs :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using IAccessTokenProvider = 
    Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
            this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                new HttpClient(),
                sp.GetRequiredService<IAuthenticationProvider>(),
                baseUrl);
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(RequestInformation request, 
            Dictionary<string, object>? additionalAuthenticationContext = null, 
            CancellationToken cancellationToken = default)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                {
                    Scopes = 
                        config.GetSection("MicrosoftGraph:Scopes").Get<string[]>() ??
                        [ "user.read" ]
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Add("Authorization", 
                    $"{CoreConstants.Headers.Bearer} {token.Value}");
            }
        }
    }
}

Important

Veuillez consulter la section comparaison entre DefaultAccessTokenScopes et AdditionalScopesToConsent pour savoir pourquoi le code précédent utilise DefaultAccessTokenScopes pour ajouter les étendues plutôt que AdditionalScopesToConsent.

Dans le fichier Program, ajoutez les services client Graph et la configuration avec la méthode d'extension AddGraphClient. Le code suivant utilise par défaut l'adresse de base Microsoft Graph Version 1.0 et les étendues User.Read si ces paramètres ne sont pas trouvés dans le fichier de paramètres de l'application :

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Appeler Graph API depuis un composant à l'aide du Graph SDK

Le composant UserData suivant utilise un GraphServiceClient injecté pour obtenir les données du profil ME-ID de l'utilisateur et afficher son numéro de téléphone mobile.

Pour tout utilisateur de test que vous créez dans ME-ID, assurez-vous d'ajouter un numéro de téléphone mobile au profil ME-ID de l'utilisateur dans le portail Azure.

UserData.razor :

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.Models.User? user;

    protected override async Task OnInitializedAsync()
    {
        user = await Client.Me.GetAsync();
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Conseil

Pour ajouter des utilisateurs à une application, veuillez consulter la section Attribuer des utilisateurs à un enregistrement d'application avec ou sans rôles d'application.

Lorsque vous effectuez des tests locaux avec le Graph SDK, nous vous recommandons d'utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d'éviter que des cookies persistants n'interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Personnaliser les revendications utilisateur à l'aide du Graph SDK

Dans l'exemple suivant, l'application crée des revendications pour le numéro de téléphone mobile et le lieu de travail d'un utilisateur à partir des données du profil utilisateur ME-ID. L'application doit disposer de l'étendue Graph API User.Read configurée dans ME-ID. Les utilisateurs de test pour ce scénario doivent avoir un numéro de téléphone mobile et un lieu de travail renseignés dans leur profil ME-ID, ce qui peut être fait via le portail Azure.

Dans la fabrique de comptes utilisateur personnalisée suivante :

  • Un ILogger (logger) est inclus pour faciliter la journalisation des informations ou erreurs dans la méthode CreateUserAsync.
  • Dans le cas où une AccessTokenNotAvailableException est levée, l'utilisateur est redirigé vers le fournisseur d'identité pour s'authentifier sur son compte. Des actions supplémentaires ou différentes peuvent être prises lorsque la demande de jeton d'accès échoue. Par exemple, l'application peut consigner le AccessTokenNotAvailableException et créer un ticket de support pour une enquête plus approfondie.
  • L'objet RemoteUserAccount du framework représente le compte utilisateur. Si l'application requiert une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez-la par RemoteUserAccount dans le code suivant.

CustomAccountFactory.cs :

using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions.Authentication;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger,
        IConfiguration config) 
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;
    private readonly string? baseUrl = string.Join("/",
        config.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
            "https://graph.microsoft.com",
        config.GetSection("MicrosoftGraph")["Version"] ??
            "v1.0");

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null &&
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null && !string.IsNullOrEmpty(baseUrl))
            {
                try
                {
                    var client = new GraphServiceClient(
                        new HttpClient(),
                        serviceProvider
                            .GetRequiredService<IAuthenticationProvider>(),
                        baseUrl);

                    var user = await client.Me.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Configurez l'authentification MSAL pour utiliser la fabrique de compte utilisateur personnalisée.

Vérifiez que le fichier Program utilise l’espace de noms Microsoft.AspNetCore.Components.WebAssembly.Authentication :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'exemple de cette section repose sur l'approche consistant à lire l'URL de base avec la version et les étendues depuis la configuration de l'application via la section MicrosoftGraph dans le fichier wwwroot/appsettings.json. Les lignes suivantes devraient déjà figurer dans le fichier Program si vous avez suivi les instructions précédentes de cette rubrique :

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Dans le fichier Program, localisez l'appel à la méthode d'extension AddMsalAuthentication. Mettez à jour le code comme suit, en y ajoutant un appel à AddAccountClaimsPrincipalFactory qui ajoute une fabrique de revendications principale de compte avec le CustomAccountFactory.

Si l'application utilise une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez cette classe dans le code suivant par RemoteUserAccount.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Vous pouvez utiliser le composant UserClaims suivant pour étudier les revendications de l'utilisateur après l'authentification avec ME-ID :

UserClaims.razor :

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

<h1>User Claims</h1>

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

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

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Lorsque vous effectuez des tests locaux avec le Graph SDK, nous vous recommandons d'utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d'éviter que des cookies persistants n'interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Les conseils suivants s'appliquent à Microsoft Graph v4. Si vous mettez à jour une application du SDK v4 vers v5, veuillez consulter le journal des modifications et guide de mise à niveau de Microsoft Graph .NET SDK v5.

Le kit Microsoft Graph SDK destiné aux applications Blazor s'appelle la bibliothèque cliente Microsoft Graph .NET.

Les exemples Graph SDK nécessitent les références de package suivantes dans l’application autonome Blazor WebAssembly. Les deux premiers packages sont déjà référencés si l'application a été activée pour l'authentification MSAL, par exemple lors de la création de l'application en suivant les conseils de la rubrique Sécuriser une application Blazor WebAssembly autonome ASP.NET Core avec Microsoft Entra ID.

Les exemples Graph SDK nécessitent les références de package suivantes dans l’application autonome Blazor WebAssembly ou l’application Client d’une solution hébergée Blazor WebAssembly. Les deux premiers packages sont déjà référencés si l'application a été activée pour l'authentification MSAL, par exemple lors de la création de l'application en suivant les conseils de la rubrique Sécuriser une application Blazor WebAssembly autonome ASP.NET Core avec Microsoft Entra ID.

Note

Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.

Dans le portail Azure, accordez des autorisations déléguées (étendues) pour les données Microsoft Graph auxquelles l'application doit pouvoir accéder au nom d'un utilisateur. Pour l'exemple de cette rubrique, l’enregistrement de l’application doit inclure l’autorisation déléguée permettant de lire les données utilisateur (étendue Microsoft.Graph>User.Read dans Autorisations d’API, Type : Déléguée). L’étendue User.Read permet aux utilisateurs de se connecter à l’application et permet à l’application de lire le profil et les informations de l’entreprise des utilisateurs connectés. Pour plus d’informations, consultez Vue d’ensemble des autorisations et du consentement dans la plateforme d’identités Microsoft et Vue d’ensemble des autorisations Microsoft Graph.

Permissions et étendues signifient la même chose et sont interchangeables dans la documentation de sécurité et dans le Portail Azure. À moins que le texte ne fasse référence au portail Azure, cette rubrique utilise étendue/étendues pour désigner les autorisations Graph.

Les étendues ne sont pas sensibles à la casse, donc User.Read est équivalent à user.read. Vous pouvez utiliser l’un ou l’autre format, mais nous vous recommandons de faire un choix cohérent dans l’ensemble du code de l’application.

Après avoir ajouté les étendues Microsoft Graph API à l’enregistrement de l’application dans le portail Azure, ajoutez la configuration des paramètres de l’application suivante dans le fichier wwwroot/appsettings.json de l’application, qui inclut l’URL de base Graph avec la version de Microsoft Graph et les étendues. Dans l’exemple suivant, l’étendue User.Read est spécifiée pour les exemples présentés dans les sections suivantes de cette rubrique. Les étendues ne sont pas sensibles à la casse.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com",
  "Version": "{VERSION}",
  "Scopes": [
    "user.read"
  ]
}

Dans l’exemple précédent, l’espace réservé {VERSION} correspond à la version de l’API Microsoft Graph (par exemple : v1.0).

Voici un exemple de fichier de configuration complet wwwroot/appsettings.json pour une application qui utilise ME-ID comme fournisseur d’identité, où la lecture des données utilisateur (étendueuser.read) est spécifiée pour Microsoft Graph :

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com",
    "Version": "v1.0",
    "Scopes": [
      "user.read"
    ]
  }
}

Dans l’exemple précédent, l’espace réservé {TENANT ID} correspond à l’ID de répertoire (locataire), et l’espace réservé {CLIENT ID} correspond à l’ID d’application (client). Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Ajoutez la classe GraphClientExtensions suivante à l'application autonome. Les étendues sont fournies à la propriété Scopes du AccessTokenRequestOptions dans la méthode AuthenticateRequestAsync. La valeur de IHttpProvider.OverallTimeout est prolongée de la valeur par défaut de 100 secondes à 300 secondes pour laisser plus de temps à HttpClient pour recevoir une réponse de Microsoft Graph.

Ajoutez la classe GraphClientExtensions suivante à l'application autonome ou à l'application Client d'une Blazor WebAssemblysolution hébergée. Les étendues sont fournies à la propriété Scopes du AccessTokenRequestOptions dans la méthode AuthenticateRequestAsync. La valeur de IHttpProvider.OverallTimeout est prolongée de la valeur par défaut de 100 secondes à 300 secondes pour laisser plus de temps à HttpClient pour recevoir une réponse de Microsoft Graph.

Lorsqu'un jeton d'accès n'est pas obtenu, le code suivant ne définit pas d'en-tête d'autorisation Bearer pour les requêtes Graph.

GraphClientExtensions.cs :

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
        this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped<IHttpProvider, HttpClientHttpProvider>(sp =>
            new HttpClientHttpProvider(new HttpClient()));

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                baseUrl,
                sp.GetRequiredService<IAuthenticationProvider>(),
                sp.GetRequiredService<IHttpProvider>());
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(HttpRequestMessage request)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                { 
                    Scopes = config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Authorization ??= new AuthenticationHeaderValue(
                    "Bearer", token.Value);
            }
        }
    }

    private class HttpClientHttpProvider(HttpClient client) : IHttpProvider
    {
        private readonly HttpClient client = client;

        public ISerializer Serializer { get; } = new Serializer();

        public TimeSpan OverallTimeout { get; set; } = TimeSpan.FromSeconds(300);

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
        {
            return client.SendAsync(request);
        }

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
            HttpCompletionOption completionOption,
            CancellationToken cancellationToken)
        {
            return client.SendAsync(request, completionOption, cancellationToken);
        }

        public void Dispose()
        {
        }
    }
}

Important

Veuillez consulter la section comparaison entre DefaultAccessTokenScopes et AdditionalScopesToConsent pour savoir pourquoi le code précédent utilise DefaultAccessTokenScopes pour ajouter les étendues plutôt que AdditionalScopesToConsent.

Dans le fichier Program, ajoutez les services client Graph et la configuration avec la méthode d'extension AddGraphClient :

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Appeler Graph API depuis un composant à l'aide du Graph SDK

Le composant UserData suivant utilise un GraphServiceClient injecté pour obtenir les données du profil ME-ID de l'utilisateur et afficher son numéro de téléphone mobile. Pour tout utilisateur de test que vous créez dans ME-ID, assurez-vous d'ajouter un numéro de téléphone mobile au profil ME-ID de l'utilisateur dans le portail Azure.

UserData.razor :

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.User? user;

    protected override async Task OnInitializedAsync()
    {
        var request = Client.Me.Request();
        user = await request.GetAsync();
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Conseil

Pour ajouter des utilisateurs à une application, veuillez consulter la section Attribuer des utilisateurs à un enregistrement d'application avec ou sans rôles d'application.

Lorsque vous effectuez des tests locaux avec le Graph SDK, nous vous recommandons d'utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d'éviter que des cookies persistants n'interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Personnaliser les revendications utilisateur à l'aide du Graph SDK

Dans l'exemple suivant, l'application crée des revendications pour le numéro de téléphone mobile et le lieu de travail d'un utilisateur à partir des données du profil utilisateur ME-ID. L'application doit disposer de l'étendue Graph API User.Read configurée dans ME-ID. Les utilisateurs de test pour ce scénario doivent avoir un numéro de téléphone mobile et un lieu de travail renseignés dans leur profil ME-ID, ce qui peut être fait via le portail Azure.

Dans la fabrique de comptes utilisateur personnalisée suivante :

  • Un ILogger (logger) est inclus pour faciliter la journalisation des informations ou erreurs dans la méthode CreateUserAsync.
  • Dans le cas où une AccessTokenNotAvailableException est levée, l'utilisateur est redirigé vers le fournisseur d'identité pour s'authentifier sur son compte. Des actions supplémentaires ou différentes peuvent être prises lorsque la demande de jeton d'accès échoue. Par exemple, l'application peut consigner le AccessTokenNotAvailableException et créer un ticket de support pour une enquête plus approfondie.
  • L'objet RemoteUserAccount du framework représente le compte utilisateur. Si l'application requiert une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez-la par RemoteUserAccount dans le code suivant.

CustomAccountFactory.cs :

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

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor, 
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = ActivatorUtilities
                        .CreateInstance<GraphServiceClient>(serviceProvider);
                    var request = client.Me.Request();
                    var user = await request.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Configurez l'authentification MSAL pour utiliser la fabrique de compte utilisateur personnalisée.

Vérifiez que le fichier Program utilise l’espace de noms Microsoft.AspNetCore.Components.WebAssembly.Authentication :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'exemple de cette section repose sur l'approche consistant à lire l'URL de base avec la version et les étendues depuis la configuration de l'application via la section MicrosoftGraph dans le fichier wwwroot/appsettings.json. Les lignes suivantes devraient déjà figurer dans le fichier Program si vous avez suivi les instructions précédentes de cette rubrique :

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Dans le fichier Program, localisez l'appel à la méthode d'extension AddMsalAuthentication. Mettez à jour le code comme suit, en y ajoutant un appel à AddAccountClaimsPrincipalFactory qui ajoute une fabrique de revendications principale de compte avec le CustomAccountFactory.

Si l'application utilise une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez cette classe dans le code suivant par RemoteUserAccount.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Vous pouvez utiliser le composant UserClaims suivant pour étudier les revendications de l'utilisateur après l'authentification avec ME-ID :

UserClaims.razor :

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

<h1>User Claims</h1>

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

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

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Lorsque vous effectuez des tests locaux avec le Graph SDK, nous vous recommandons d'utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d'éviter que des cookies persistants n'interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Les exemples suivants utilisent un HttpClient nommé pour les appels Graph API afin d’obtenir un numéro de téléphone mobile d’utilisateur pour traiter un appel ou personnaliser les revendications utilisateur pour inclure un numéro de téléphone mobile et un lieu de travail.

Les exemples nécessitent une référence de package à Microsoft.Extensions.Http pour l’application Blazor WebAssembly autonome.

Les exemples nécessitent une référence de package à Microsoft.Extensions.Http pour l’application Blazor WebAssembly autonome ou l’application Client d’une solution Blazor WebAssembly hébergée.

Note

Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.

Dans le portail Azure, accordez des autorisations déléguées (étendues) pour les données Microsoft Graph auxquelles l'application doit pouvoir accéder au nom d'un utilisateur. Pour l'exemple de cette rubrique, l’enregistrement de l’application doit inclure l’autorisation déléguée permettant de lire les données utilisateur (étendue Microsoft.Graph>User.Read dans Autorisations d’API, Type : Déléguée). L’étendue User.Read permet aux utilisateurs de se connecter à l’application et permet à l’application de lire le profil et les informations de l’entreprise des utilisateurs connectés. Pour plus d’informations, consultez Vue d’ensemble des autorisations et du consentement dans la plateforme d’identités Microsoft et Vue d’ensemble des autorisations Microsoft Graph.

Permissions et étendues signifient la même chose et sont interchangeables dans la documentation de sécurité et dans le Portail Azure. À moins que le texte ne fasse référence au portail Azure, cette rubrique utilise étendue/étendues pour désigner les autorisations Graph.

Les étendues ne sont pas sensibles à la casse, donc User.Read est équivalent à user.read. Vous pouvez utiliser l’un ou l’autre format, mais nous vous recommandons de faire un choix cohérent dans l’ensemble du code de l’application.

Après avoir ajouté les étendues Microsoft Graph API à l’enregistrement de l’application dans le portail Azure, ajoutez la configuration des paramètres de l’application suivante dans le fichier wwwroot/appsettings.json de l’application, qui inclut l’URL de base Graph avec la version de Microsoft Graph et les étendues. Dans l’exemple suivant, l’étendue User.Read est spécifiée pour les exemples présentés dans les sections suivantes de cette rubrique. Les étendues ne sont pas sensibles à la casse.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com",
  "Version": "{VERSION}",
  "Scopes": [
    "user.read"
  ]
}

Dans l’exemple précédent, l’espace réservé {VERSION} correspond à la version de l’API Microsoft Graph (par exemple : v1.0).

Voici un exemple de fichier de configuration complet wwwroot/appsettings.json pour une application qui utilise ME-ID comme fournisseur d’identité, où la lecture des données utilisateur (étendueuser.read) est spécifiée pour Microsoft Graph :

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com",
    "Version": "v1.0",
    "Scopes": [
      "user.read"
    ]
  }
}

Dans l’exemple précédent, l’espace réservé {TENANT ID} correspond à l’ID de répertoire (locataire), et l’espace réservé {CLIENT ID} correspond à l’ID d’application (client). Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Créez la classe GraphAuthorizationMessageHandler suivante et la configuration du projet dans le fichier Program pour interagir avec Graph API. L’URL de base et les étendues sont fournies au gestionnaire via la configuration.

GraphAuthorizationMessageHandler.cs :

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

namespace BlazorSample;

public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
        NavigationManager navigation, IConfiguration config)
        : base(provider, navigation)
    {
        ConfigureHandler(
            authorizedUrls: [ 
                string.Join("/",
                    config.GetSection("MicrosoftGraph")["BaseUrl"] ??
                        "https://graph.microsoft.com",
                    config.GetSection("MicrosoftGraph")["Version"] ??
                        "v1.0")
            ],
            scopes: config.GetSection("MicrosoftGraph:Scopes")
                        .Get<List<string>>() ?? [ "user.read" ]);
    }
}

La barre oblique finale (/) de l’URL autorisée est requise. Le code précédent génère l’URL autorisée suivante à partir de la configuration des paramètres de l’application ou utilise par défaut l’URL suivante si la configuration est absente : https://graph.microsoft.com/v1.0/.

Dans le fichier Program, configurez le HttpClient nommé pour Graph API :

builder.Services.AddTransient<GraphAuthorizationMessageHandler>();

builder.Services.AddHttpClient("GraphAPI",
        client => client.BaseAddress = new Uri(
            string.Join("/",
                builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
                    "https://graph.microsoft.com",
                builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
                    "v1.0",
                string.Empty)))
    .AddHttpMessageHandler<GraphAuthorizationMessageHandler>();

Dans l’exemple précédent, le GraphAuthorizationMessageHandlerDelegatingHandler est inscrit en tant que service temporaire pour AddHttpMessageHandler. L’inscription temporaire est recommandée pour IHttpClientFactory, qui gère ses propres étendues de DI. Pour plus d'informations, reportez-vous aux ressources suivantes :

Une barre oblique finale (/) sur l’adresse de base est requise. Dans le code précédent, le troisième argument de string.Join est string.Empty pour garantir la présence de la barre oblique finale : https://graph.microsoft.com/v1.0/.

Appeler Graph API depuis un composant à l’aide d’un HttpClient nommé

La classe UserInfo.cs désigne les propriétés requises du profil utilisateur à l’aide de l’attribut JsonPropertyNameAttribute et du nom JSON utilisé par ME-ID. L’exemple suivant définit les propriétés pour le numéro de téléphone mobile et le lieu de travail de l’utilisateur.

UserInfo.cs :

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

Dans le composant UserData suivant, un HttpClient est créé pour Graph API afin d’émettre une requête pour les données du profil utilisateur. La ressource me (me) est ajoutée à l’URL de base avec la version pour la requête Graph API. Les données JSON renvoyées par Graph sont désérialisées dans les propriétés de la classe UserInfo. Dans l’exemple suivant, le numéro de téléphone mobile est obtenu. Vous pouvez ajouter un code similaire pour inclure le lieu de travail du profil ME-ID de l’utilisateur si vous le souhaitez (userInfo.OfficeLocation). Si la demande de jeton d’accès échoue, l’utilisateur est redirigé vers l’application pour une nouvelle authentification.

UserData.razor :

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@attribute [Authorize]
@inject IConfiguration Config
@inject IHttpClientFactory ClientFactory

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(userInfo?.MobilePhone))
{
    <p>Mobile Phone: @userInfo.MobilePhone</p>
}

@code {
    private UserInfo? userInfo;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var client = ClientFactory.CreateClient("GraphAPI");

            userInfo = await client.GetFromJsonAsync<UserInfo>("me");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Conseil

Pour ajouter des utilisateurs à une application, veuillez consulter la section Attribuer des utilisateurs à un enregistrement d'application avec ou sans rôles d'application.

La séquence suivante décrit le flux d’un nouvel utilisateur pour les étendues Graph API :

  1. Le nouvel utilisateur se connecte à l’application pour la première fois.
  2. L’utilisateur consent à l’utilisation de l’application via l’interface de consentement Azure.
  3. L’utilisateur accède à une page de composant qui demande pour la première fois des données Graph API.
  4. L’utilisateur est redirigé vers l’interface de consentement Azure pour donner son accord sur les étendues Graph API.
  5. Les données utilisateur Graph API sont renvoyées.

Si vous préférez que l’approvisionnement des étendues (consentement aux étendues Graph API) ait lieu lors de la première connexion, fournissez les étendues à l’authentification MSAL comme étendues par défaut du jeton d’accès dans le fichier Program :

+ var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
+     .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);

+   foreach (var scope in scopes)
+   {
+       options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
+   }
});

Important

Veuillez consulter la section comparaison entre DefaultAccessTokenScopes et AdditionalScopesToConsent pour savoir pourquoi le code précédent utilise DefaultAccessTokenScopes pour ajouter les étendues plutôt que AdditionalScopesToConsent.

Lorsque les modifications précédentes sont apportées à l’application, le flux utilisateur adopte la séquence suivante :

  1. Le nouvel utilisateur se connecte à l’application pour la première fois.
  2. L’utilisateur consent à l’utilisation de l’application et aux étendues Graph API dans l’interface de consentement Azure.
  3. L’utilisateur accède à une page de composant qui demande pour la première fois des données Graph API.
  4. Les données utilisateur Graph API sont renvoyées.

Lorsque vous effectuez des tests locaux avec Graph API, nous vous recommandons d’utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d’éviter que des cookies persistants n’interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Personnaliser les revendications utilisateur à l’aide d’un HttpClient nommé

Dans l'exemple suivant, l'application crée des revendications pour le numéro de téléphone mobile et le lieu de travail de l'utilisateur à partir des données du profil utilisateur ME-ID. L'application doit disposer de l'étendue Graph API User.Read configurée dans ME-ID. Les comptes utilisateur de test dans ME-ID doivent contenir un numéro de téléphone mobile et un lieu de travail, qui peuvent être ajoutés via le portail Azure à leurs profils utilisateur.

Si vous n’avez pas encore ajouté la classe UserInfo à l’application en suivant les instructions précédentes de cette rubrique, ajoutez la classe suivante et désignez les propriétés requises du profil utilisateur à l’aide de l’attribut JsonPropertyNameAttribute et du nom JSON utilisé par ME-ID. L’exemple suivant définit les propriétés pour le numéro de téléphone mobile et le lieu de travail de l’utilisateur.

UserInfo.cs :

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

Dans la fabrique de comptes utilisateur personnalisée suivante :

  • Un ILogger (logger) est inclus pour faciliter la journalisation des informations ou erreurs dans la méthode CreateUserAsync.
  • Dans le cas où une AccessTokenNotAvailableException est levée, l'utilisateur est redirigé vers le fournisseur d'identité pour s'authentifier sur son compte. Des actions supplémentaires ou différentes peuvent être prises lorsque la demande de jeton d'accès échoue. Par exemple, l'application peut consigner le AccessTokenNotAvailableException et créer un ticket de support pour une enquête plus approfondie.
  • L'objet RemoteUserAccount du framework représente le compte utilisateur. Si l’application requiert une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez cette classe par RemoteUserAccount dans le code suivant.

CustomAccountFactory.cs :

using System.Net.Http.Json;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IHttpClientFactory clientFactory,
        ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IHttpClientFactory clientFactory = clientFactory;

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = clientFactory.CreateClient("GraphAPI");

                    var userInfo = await client.GetFromJsonAsync<UserInfo>("me");

                    if (userInfo is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            userInfo.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            userInfo.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

L’authentification MSAL est configurée pour utiliser la fabrique de compte utilisateur personnalisée. Commencez par vérifier que le fichier Program utilise l’espace de noms Microsoft.AspNetCore.Components.WebAssembly.Authentication :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Dans le fichier Program, localisez l'appel à la méthode d'extension AddMsalAuthentication. Mettez à jour le code comme suit, en y ajoutant un appel à AddAccountClaimsPrincipalFactory qui ajoute une fabrique de revendications principale de compte avec le CustomAccountFactory.

Si l’application utilise une classe de compte utilisateur personnalisée qui hérite de RemoteUserAccount, remplacez la classe de compte personnalisée de votre application par RemoteUserAccount dans le code suivant.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState, 
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, 
        CustomAccountFactory>();

L’exemple précédent s’applique à une application qui utilise l’authentification ME-ID avec MSAL. Des schémas similaires existent pour l’authentification OIDC et API. Pour en savoir plus, veuillez consulter les exemples de la section Personnaliser l’utilisateur à l’aide d’une revendication de charge utile de l’article Scénarios de sécurité supplémentaires pour ASP.NET Core Blazor WebAssembly.

Vous pouvez utiliser le composant UserClaims suivant pour étudier les revendications de l'utilisateur après l'authentification avec ME-ID :

UserClaims.razor :

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

<h1>User Claims</h1>

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

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

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Ajoutez un lien vers la page du composant dans le composant NavMenu (Layout/NavMenu.razor) :

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Lorsque vous effectuez des tests locaux avec Graph API, nous vous recommandons d’utiliser une nouvelle session de navigation InPrivate/incognito pour chaque test afin d’éviter que des cookies persistants n’interfèrent avec les tests. Pour en savoir plus, veuillez consulter la section Sécuriser une application autonome ASP.NET Core Blazor WebAssembly avec Microsoft Entra ID.

Attribuer des utilisateurs à un enregistrement d'application avec ou sans rôles d’application

Vous pouvez ajouter des utilisateurs à un enregistrement d’application et leur attribuer des rôles en suivant les étapes ci-dessous dans le portail Azure.

Pour ajouter un utilisateur, sélectionnez Utilisateurs dans la zone ME-ID du portail Azure :

  1. Sélectionnez Nouvel utilisateur>Créer un utilisateur.
  2. Utilisez le modèle Créer un utilisateur.
  3. Fournissez les informations de l’utilisateur dans la zone Identity.
  4. Vous pouvez générer un mot de passe initial ou attribuer un mot de passe initial que l’utilisateur modifiera lors de sa première connexion. Si vous utilisez le mot de passe généré par le portail, notez-le maintenant.
  5. Sélectionnez Créer pour créer l’utilisateur. Lorsque l’interface Créer un nouvel utilisateur se ferme, sélectionnez Actualiser pour mettre à jour la liste des utilisateurs et afficher le nouvel utilisateur.
  6. Pour les exemples de cette rubrique, attribuez un numéro de téléphone mobile au nouvel utilisateur en sélectionnant son nom dans la liste, puis en sélectionnant Propriétés et en modifiant les informations de contact pour ajouter un numéro de téléphone mobile.

Pour attribuer des utilisateurs à l’application sans rôles d’application :

  1. Dans la zone ME-ID du portail Azure, ouvrez Applications d’entreprise.
  2. Sélectionnez l’application dans la liste.
  3. Sélectionnez Utilisateurs et groupes.
  4. Sélectionner Ajouter un utilisateur/groupe.
  5. Sélectionnez un utilisateur.
  6. Sélectionnez le bouton Attribuer.

Pour attribuer des utilisateurs à l’application avec rôles d’application :

  1. Ajoutez des rôles à l’enregistrement de l’application dans le portail Azure en suivant les instructions de la rubrique ASP.NET Core Blazor WebAssembly avec les groupes et rôles Microsoft Entra ID.
  2. Dans la zone ME-ID du portail Azure, ouvrez Applications d’entreprise.
  3. Sélectionnez l’application dans la liste.
  4. Sélectionnez Utilisateurs et groupes.
  5. Sélectionner Ajouter un utilisateur/groupe.
  6. Sélectionnez un utilisateur, puis sélectionnez son rôle pour accéder à l’application. Plusieurs rôles peuvent être attribués à un utilisateur en répétant le processus d’ajout de l’utilisateur à l’application jusqu’à ce que tous les rôles lui aient été attribués. Les utilisateurs disposant de plusieurs rôles sont répertoriés une fois par rôle dans la liste Utilisateurs et groupes de l’application.
  7. Sélectionnez le bouton Attribuer.

DefaultAccessTokenScopes Vs AdditionalScopesToConsent

Les exemples de cette rubrique approvisionnent les étendues Graph API à l’aide de DefaultAccessTokenScopes, et non de AdditionalScopesToConsent.

AdditionalScopesToConsent n’est pas utilisé car il ne permet pas d’approvisionner les étendues Graph API pour les utilisateurs lorsqu’ils se connectent à l’application pour la première fois avec MSAL via l’interface de consentement Azure. Lorsque l’utilisateur tente d’accéder à Graph API pour la première fois avec le Graph SDK, une exception se produit :

Microsoft.Graph.Models.ODataErrors.ODataError: Access token is empty.

Une fois que l’utilisateur a donné son consentement pour les étendues Graph API fournies via DefaultAccessTokenScopes, l’application peut utiliser AdditionalScopesToConsent lors d’une connexion ultérieure. Cependant, modifier le code de l’application n’a pas de sens pour une application de production nécessitant l’ajout périodique de nouveaux utilisateurs avec des étendues Graph déléguées ou l’ajout de nouvelles étendues Graph API déléguées à l’application.

La discussion précédente sur la façon d’approvisionner les étendues d’accès Graph API lors de la première connexion de l’utilisateur s’applique uniquement aux cas suivants :

  • Les applications qui utilisent le Graph SDK.
  • Les applications qui utilisent un HttpClient nommé pour accéder à Graph API et demandent aux utilisateurs de consentir aux étendues Graph lors de leur première connexion.

Lorsqu’un HttpClient nommé est utilisé sans demander le consentement des utilisateurs pour les étendues Graph lors de la première connexion, les utilisateurs sont redirigés vers l’interface de consentement Azure pour consentir aux étendues Graph API lorsqu’ils demandent pour la première fois un accès à Graph API via le DelegatingHandler du HttpClient nommé et préconfiguré. Lorsque les étendues Graph ne sont pas consenties initialement avec l’approche HttpClient nommée, ni DefaultAccessTokenScopes ni AdditionalScopesToConsent ne sont appelés par l’application. Pour en savoir plus, veuillez consulter la section couverture de l’approche HttpClient nommée dans cet article.

Solutions Blazor WebAssembly hébergées

Les exemples de cette rubrique concernent l’utilisation du Graph SDK ou d’un HttpClient nommé avec Graph API directement depuis une application Blazor WebAssembly autonome ou directement depuis l’application Client d’une Blazor WebAssemblysolution hébergée. Un scénario supplémentaire non couvert par cette rubrique consiste à faire en sorte qu’une application Client d’une solution hébergée appelle l’application Server de la solution via une API web, puis que l’application Server utilise le SDK/API Graph pour appeler Microsoft Graph et renvoyer les données à l’application Client. Bien que cette approche soit prise en charge, elle n’est pas traitée dans cette rubrique. Si vous souhaitez adopter cette approche :

  • Suivez les instructions de la rubrique Appeler une API web depuis une application ASP.NET Core Blazor pour les aspects liés à l’API web concernant l’émission de requêtes à l’application Server depuis l’application Client et le retour des données à l’application Client.
  • Suivez les instructions de la documentation principale de Microsoft Graph pour utiliser le Graph SDK avec une application ASP.NET Core classique, ce qui, dans ce scénario, correspond à l’application Server de la solution. Si vous utilisez le modèle de projet Blazor WebAssembly pour créer la solution Blazor WebAssembly hébergée (ASP.NET Core Hosted/-h|--hosted) avec l’autorisation organisationnelle (organisation unique/SingleOrg ou plusieurs organisations/MultiOrg) et l’option Microsoft Graph (plateforme d’identités Microsoft>Services connectés>Ajouter des autorisations Microsoft Graph dans Visual Studio ou l’option --calls-graph avec la commande .NET CLI dotnet new), l’application Server de la solution est configurée pour utiliser le Kit de développement logiciel (SDK) Graph lorsque la solution est créée à partir du modèle de projet.

Ressources supplémentaires

Règle générale

  • Documentation sur l’API Microsoft Graph
  • Exemple d'application Blazor WebAssembly avec Microsoft Graph : Cet exemple montre comment utiliser le SDK Microsoft Graph .NET pour accéder aux données Office 365 depuis des applications Blazor WebAssembly.
  • Tutoriel pour développer des applications .NET avec Microsoft Graph et exemple d'application ASP.NET Core Microsoft Graph : Ces ressources sont particulièrement adaptées aux solutionsBlazor WebAssembly hébergées, où l'application Server est configurée pour accéder à Microsoft Graph comme une application ASP.NET Core classique au nom de l'application Client. L'application Client utilise une API web pour effectuer des requêtes vers l'application Server pour obtenir les données Graph. Bien que ces ressources ne s'appliquent pas directement à l’appel de Graph depuis des applications client-sideBlazor WebAssembly, la configuration ME-ID et les pratiques de codage Microsoft Graph qu’elles contiennent sont pertinentes pour les applications Blazor WebAssembly autonomes et doivent être consultées pour les bonnes pratiques générales.

Conseils de sécurité