Usare l'API Graph con ASP.NET Core Blazor WebAssembly

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Questo articolo illustra come usare Microsoft Graph nelle Blazor WebAssembly app, che consente alle app di accedere alle risorse di Microsoft Cloud.

Sono trattati due approcci:

  • Graph SDK: Microsoft Graph SDK semplifica la creazione di app di alta qualità, efficienti e resilienti che accedono a Microsoft Graph. Selezionare il pulsante Graph SDK nella parte superiore di questo articolo per adottare questo approccio.

  • HttpClient denominato per l'API Graph: un client denominato HttpClient può inviare richieste alle API Microsoft Graph direttamente a Microsoft Graph. Seleziona il pulsante Named HttpClient with API Graph nella parte superiore di questo articolo per adottare questo approccio.

Le linee guida contenute in questo articolo non sono progettate per sostituire la documentazione di Microsoft Graph e le linee guida sulla sicurezza di Azure in altri set di documentazione Microsoft. Valutare le indicazioni sulla sicurezza nella sezione Risorse aggiuntive di questo articolo prima di implementare Microsoft Graph in un ambiente di produzione. Seguire le procedure consigliate di Microsoft per limitare le vulnerabilità delle app.

Altri approcci per l'uso di Microsoft Graph e Blazor WebAssembly sono disponibili negli esempi di Microsoft Graph e Azure seguenti:

Per fornire commenti e suggerimenti su uno dei due esempi precedenti, aprire un problema nel repository GitHub dell'esempio. Se stai segnalando un problema per un sample Azure, includi un link al sample nel commento iniziale perché il repository dei sample Azure (Azure-Samples) contiene molti sample. Descrivere il problema in dettaglio e includere il codice di esempio in base alle esigenze. Inserire un'app minima in GitHub che riproduce il problema o l'errore. Assicurarsi di rimuovere i dati di configurazione dell'account di Azure dall'esempio prima di eseguirne il commit nel repository pubblico.

Per fornire feedback o richiedere assistenza per questo articolo o per ASP.NET Core, consulta Informazioni di base su ASP.NET Core Blazor.

Importante

Gli scenari descritti in questo articolo si applicano all'uso di Microsoft Entra (ME-ID) come provider di identità, non AAD B2C. L'uso di Microsoft Graph con un'app lato Blazor WebAssembly client e il provider di identità AAD B2C non è attualmente supportato perché l'app richiederebbe un segreto client, che non può essere protetto nell'app lato Blazor client. Per un'app AAD B2C autonoma Blazor WebAssembly, usa l'API Graph: crea un'API Web di back-end per accedere all'API Graph per conto degli utenti. L'app lato client consente di autenticare e autorizzare gli utenti a chiamare l'API Web per accedere in modo sicuro a Microsoft Graph e restituire i dati dall'API Web basata su server all'app lato Blazor client. Il segreto client viene mantenuto in modo sicuro nell'API Web basata su server, non nell'app Blazor nel client. Non archiviare mai un segreto del client in un'app sul lato Blazor client.

Nota

Azure Active Directory B2C non è più disponibile come servizio ai nuovi clienti a partire dal 1° maggio 2025. Per altre informazioni, vedere Azure AD B2C: Domande frequenti.

L'uso di un'app ospitata Blazor WebAssembly è supportato, in cui l'app Server usa Graph SDK/API per fornire dati Graph all'app Client tramite l'API Web. Per ulteriori informazioni, consultare la sezione soluzioni Blazor WebAssembly ospitate di questo articolo.

Gli esempi in questo articolo sfruttano le nuove funzionalità di .NET/C#. Quando si usano gli esempi con .NET 7 o versioni precedenti, sono necessarie modifiche secondarie. Tuttavia, gli esempi di testo e codice relativi all'interazione con Microsoft Graph sono gli stessi per tutte le versioni di ASP.NET Core.

Le indicazioni seguenti si applicano a Microsoft Graph v5 o versione successiva.

Microsoft Graph SDK da usare nelle app Blazor è chiamato Microsoft Graph .NET Client Library.

Gli esempi di Graph SDK richiedono i riferimenti al pacchetto seguenti nell'app autonoma Blazor WebAssembly . I primi due pacchetti sono già a cui viene fatto riferimento se l'app è stata abilitata per l'autenticazione MSAL, ad esempio quando si crea l'app seguendo le indicazioni riportate in Proteggere un'app autonoma ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Gli esempi di Graph SDK richiedono i riferimenti al pacchetto seguenti nell'app autonoma Blazor WebAssembly o nell'app Client di una soluzione ospitata Blazor WebAssembly . I primi due pacchetti sono già a cui viene fatto riferimento se l'app è stata abilitata per l'autenticazione MSAL, ad esempio quando si crea l'app seguendo le indicazioni riportate in Proteggere un'app autonoma ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Nota

Il pacchetto Microsoft.Authentication.WebAssembly.Msal aggiunge in modo transitivo il pacchetto Microsoft.AspNetCore.Components.WebAssembly.Authentication all'app.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Nella portale di Azure concedere autorizzazioni delegate (ambiti)† per i dati di Microsoft Graph a cui l'app deve poter accedere per conto di un utente. Per l'esempio in questo articolo, la registrazione dell'app deve includere l'autorizzazione delegata per leggere i dati utente (Microsoft.Graph>User.Read ambito nelle autorizzazioni API, Tipo: Delegato). L'ambito User.Read consente agli utenti di accedere all'app e consente all'app di leggere il profilo e le informazioni aziendali degli utenti connessi. Per altre informazioni, vedere Panoramica delle autorizzazioni e del consenso in Microsoft Identity Platform e panoramica delle autorizzazioni di Microsoft Graph.

†Autorizzazioni e ambiti significano la stessa cosa e vengono usati in modo intercambiabile nella documentazione sulla sicurezza e nel portale di Azure. A meno che il testo non faccia riferimento al portale di Azure, questo articolo usa scope/scopes per riferirsi alle autorizzazioni di Graph.

Gli ambiti non fanno distinzione tra maiuscole e minuscole, quindi User.Read equivale a user.read. È possibile usare entrambi i formati, ma è consigliabile scegliere in modo coerente il codice dell'applicazione.

Dopo aver aggiunto le autorizzazioni dell'API Microsoft Graph alla registrazione dell'app nel portale di Azure, aggiungere al file wwwroot/appsettings.json dell'app la seguente configurazione delle impostazioni dell'app, che include l'URL di base di Graph con la versione di Microsoft Graph e le autorizzazioni. Nell'esempio seguente viene specificato l'ambito User.Read per gli esempi nelle sezioni successive di questo articolo. Gli ambiti non fanno distinzione tra maiuscole e minuscole.

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

Nell'esempio precedente il {VERSION} segnaposto è la versione dell'API Microsoft Graph (ad esempio: v1.0).

Di seguito è riportato un esempio di file di configurazione completo wwwroot/appsettings.json per un'app che usa ME-ID come provider di identità, in cui la lettura dei dati utente (user.read ambito) viene specificata per 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"
    ]
  }
}

Nell'esempio precedente il {TENANT ID} segnaposto è l'ID directory (tenant) e il {CLIENT ID} segnaposto è l'ID applicazione (client). Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Aggiungere la classe seguente GraphClientExtensions all'app autonoma. Gli ambiti vengono passati alla proprietà Scopes di AccessTokenRequestOptions nel metodo AuthenticateRequestAsync.

Aggiungere la seguente classe GraphClientExtensions all'app autonoma o Client all'app di una Blazor WebAssemblysoluzione ospitata. Gli ambiti vengono forniti alla proprietà Scopes di AccessTokenRequestOptions nel metodo AuthenticateRequestAsync.

Quando non viene ottenuto un token di accesso, il codice seguente non imposta un'intestazione Bearer di autorizzazione per le richieste 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}");
            }
        }
    }
}

Importante

Vedere la sezione DefaultAccessTokenScopes rispetto a AdditionalScopesToConsent per una spiegazione del motivo per cui il codice precedente usa per aggiungere gli ambiti anziché .

Nel file Program, aggiungi i servizi client di Graph e la configurazione con il metodo di estensione AddGraphClient. Per impostazione predefinita, il codice seguente usa l'indirizzo di base di Microsoft Graph versione 1.0 e gli ambiti User.Read se queste impostazioni non sono presenti nel file di impostazioni dell'app:

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);

Chiamare l'API Graph da un componente usando Graph SDK

Il componente seguente UserData usa un oggetto inserito GraphServiceClient per ottenere i dati del profilo ME-ID dell'utente e visualizzare il numero di telefono cellulare.

Per qualsiasi utente di test creato in ME-ID, assicurarsi di assegnare al profilo ME-ID dell'utente un numero di telefono cellulare nel portale di 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();
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Suggerimento

Per aggiungere utenti a un'app, vedi la sezione Assegnare utenti a una registrazione di app con o senza ruoli dell'app.

Quando si esegue il test con Graph SDK in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Personalizzare le attestazioni utente con Graph SDK

Nell'esempio seguente l'app crea le attestazioni relative al numero di telefono cellulare e alla posizione dell'ufficio per un utente dai dati del profilo utente ME-ID. L'app deve avere l'ambito dell'API User.Read Graph configurato in ME-ID. Tutti gli utenti di test per questo scenario devono avere un numero di telefono cellulare e la posizione dell'ufficio nel profilo ME-ID, che può essere aggiunto tramite il portale di Azure.

Nella seguente factory personalizzata per l'account utente:

  • Un ILogger (logger) è incluso per comodità nel caso in cui si vogliano registrare informazioni o errori nel metodo CreateUserAsync.
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al provider di identità per accedere al proprio account. È possibile eseguire azioni aggiuntive o diverse quando la richiesta di un token di accesso non riesce. Ad esempio, l'app può registrare il AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori analisi.
  • Il RemoteUserAccount del framework rappresenta l'account dell'utente. Se l'app richiede una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata per RemoteUserAccount nel codice seguente.

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;
    }
}

Configurare l'autenticazione MSAL per usare la factory personalizzata dell'account utente.

Verificare che il file Program utilizzi il namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'esempio in questa sezione si basa sull'approccio che prevede la lettura dell'URL di base con versione e scope dalla configurazione dell'applicazione tramite la sezione MicrosoftGraph del file wwwroot/appsettings.json. Le righe seguenti dovrebbero essere già presenti nel Program file seguendo le indicazioni riportate in precedenza in questo articolo:

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);

Nel file Program, individuare la chiamata al metodo di estensione AddMsalAuthentication. Aggiornare il codice come segue, includendo una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory del principal delle attestazioni dell'account con CustomAccountFactory.

Se l'app usa una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata per RemoteUserAccount nel codice seguente.

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

È possibile usare il componente seguente UserClaims per studiare le attestazioni dell'utente dopo l'autenticazione dell'utente con 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;
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Quando si esegue il test con Graph SDK in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Le indicazioni seguenti si applicano a Microsoft Graph v4. Se si sta aggiornando un'app dall'SDK v4 alla versione 5 o successiva, vedere le risorse seguenti:

Microsoft Graph SDK da usare nelle app Blazor è chiamato Microsoft Graph .NET Client Library.

Gli esempi di Graph SDK richiedono i riferimenti al pacchetto seguenti nell'app autonoma Blazor WebAssembly . I primi due pacchetti sono già a cui viene fatto riferimento se l'app è stata abilitata per l'autenticazione MSAL, ad esempio quando si crea l'app seguendo le indicazioni riportate in Proteggere un'app autonoma ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Gli esempi di Graph SDK richiedono i riferimenti al pacchetto seguenti nell'app autonoma Blazor WebAssembly o nell'app Client di una soluzione ospitata Blazor WebAssembly . I primi due pacchetti sono già a cui viene fatto riferimento se l'app è stata abilitata per l'autenticazione MSAL, ad esempio quando si crea l'app seguendo le indicazioni riportate in Proteggere un'app autonoma ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Nella portale di Azure concedere autorizzazioni delegate (ambiti)† per i dati di Microsoft Graph a cui l'app deve poter accedere per conto di un utente. Per l'esempio in questo articolo, la registrazione dell'app deve includere l'autorizzazione delegata per leggere i dati utente (Microsoft.Graph>User.Read ambito nelle autorizzazioni API, Tipo: Delegato). L'ambito User.Read consente agli utenti di accedere all'app e consente all'app di leggere il profilo e le informazioni aziendali degli utenti connessi. Per altre informazioni, vedere Panoramica delle autorizzazioni e del consenso in Microsoft Identity Platform e panoramica delle autorizzazioni di Microsoft Graph.

†Autorizzazioni e ambiti significano la stessa cosa e sono usati in modo intercambiabile nella documentazione sulla sicurezza e nel portale di Azure. A meno che il testo non faccia riferimento al portale di Azure, questo articolo usa scope/scopes quando si riferisce alle autorizzazioni di Graph.

Gli scope non distinguono tra maiuscole e minuscole, quindi User.Read è uguale a user.read. È possibile usare entrambi i formati, ma è consigliabile scegliere in modo coerente il codice dell'applicazione.

Dopo aver aggiunto gli ambiti dell'API Microsoft Graph alla registrazione dell'app nel portale di Azure, aggiungere al file wwwroot/appsettings.json dell'app la seguente configurazione delle impostazioni dell'app, che include l'URL di base di Graph insieme alla versione di Microsoft Graph e agli ambiti. Nell'esempio seguente viene specificato l'ambito User.Read per gli esempi nelle sezioni successive di questo articolo. Gli ambiti non fanno distinzione tra maiuscole e minuscole.

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

Nell'esempio precedente il {VERSION} segnaposto è la versione dell'API Microsoft Graph (ad esempio: v1.0).

Di seguito è riportato un esempio di file di configurazione completo wwwroot/appsettings.json per un'app che usa ME-ID come provider di identità, in cui la lettura dei dati utente (user.read ambito) viene specificata per 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"
    ]
  }
}

Nell'esempio precedente il {TENANT ID} segnaposto è l'ID directory (tenant) e il {CLIENT ID} segnaposto è l'ID applicazione (client). Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Aggiungere la classe seguente GraphClientExtensions all'app autonoma. Gli ambiti sono forniti alla proprietà Scopes di AccessTokenRequestOptions nel metodo AuthenticateRequestAsync. IHttpProvider.OverallTimeout viene esteso dal valore predefinito di 100 secondi a 300 secondi per dare a HttpClient più tempo per ricevere una risposta da Microsoft Graph.

Aggiungere la seguente classe GraphClientExtensions all'app autonoma o Client all'app di una Blazor WebAssemblysoluzione ospitata. Gli ambiti vengono forniti alla proprietà Scopes di AccessTokenRequestOptions nel metodo AuthenticateRequestAsync. Il IHttpProvider.OverallTimeout viene esteso dal valore predefinito di 100 secondi a 300 secondi per dare a HttpClient più tempo per ricevere una risposta da Microsoft Graph.

Quando non viene ottenuto un token di accesso, il codice seguente non imposta un'intestazione Bearer di autorizzazione per le richieste 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()
        {
        }
    }
}

Importante

Vedere la sezione DefaultAccessTokenScopes rispetto a AdditionalScopesToConsent per una spiegazione del motivo per cui il codice precedente usa per aggiungere gli ambiti anziché .

Nel file Program, aggiungere i servizi client di Graph e la configurazione con il metodo di estensione 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);

Chiamare l'API Graph da un componente usando Graph SDK

Il componente seguente UserData usa un oggetto inserito GraphServiceClient per ottenere i dati del profilo ME-ID dell'utente e visualizzare il numero di telefono cellulare. Per qualsiasi utente di test creato in ME-ID, assicurarsi di assegnare al profilo ME-ID dell'utente un numero di telefono cellulare nel portale di 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();
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Suggerimento

Per aggiungere utenti a un'app, vedere la sezione Assegnare utenti a una registrazione dell'app con o senza ruoli dell'app.

Quando si esegue il test con Graph SDK in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Personalizzare le attestazioni utente con Graph SDK

Nell'esempio seguente l'app crea le attestazioni relative al numero di telefono cellulare e alla posizione dell'ufficio per un utente dai dati del profilo utente ME-ID. L'app deve avere l'ambito dell'API User.Read Graph configurato in ME-ID. Tutti gli utenti di test per questo scenario devono avere un numero di telefono cellulare e la posizione dell'ufficio nel profilo ME-ID, che può essere aggiunto tramite il portale di Azure.

Nella seguente factory personalizzata per l'account utente:

  • Un ILogger (logger) è incluso per comodità nel caso in cui si vogliano registrare informazioni o errori nel metodo CreateUserAsync.
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al provider di identità per accedere al proprio account. È possibile eseguire azioni aggiuntive o diverse quando la richiesta di un token di accesso non riesce. Ad esempio, l'app può registrare il AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori analisi.
  • Il RemoteUserAccount del framework rappresenta l'account dell'utente. Se l'app richiede una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata per RemoteUserAccount nel codice seguente.

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;
    }
}

Configurare l'autenticazione MSAL per usare la factory personalizzata dell'account utente.

Verificare che il file Program utilizzi il namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'esempio in questa sezione si basa sull'approccio che prevede la lettura dell'URL di base con versione e scope dalla configurazione dell'applicazione tramite la sezione MicrosoftGraph del file wwwroot/appsettings.json. Le righe seguenti dovrebbero essere già presenti nel Program file seguendo le indicazioni riportate in precedenza in questo articolo:

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);

Nel file Program, individuare la chiamata al metodo di estensione AddMsalAuthentication. Aggiornare il codice come segue, includendo una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory del principal delle attestazioni dell'account con CustomAccountFactory.

Se l'app usa una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata per RemoteUserAccount nel codice seguente.

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

È possibile usare il componente seguente UserClaims per studiare le attestazioni dell'utente dopo l'autenticazione dell'utente con 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;
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Quando si esegue il test con Graph SDK in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Gli esempi seguenti usano un denominato HttpClient per le chiamate API Graph per ottenere il numero di telefono cellulare di un utente per elaborare una chiamata o per personalizzare le attestazioni di un utente in modo da includere un'attestazione di numero di telefono cellulare e un'attestazione di posizione dell'ufficio.

Gli esempi richiedono un riferimento al pacchetto Microsoft.Extensions.Http per l'app autonoma Blazor WebAssembly.

Gli esempi richiedono un riferimento al pacchetto di Microsoft.Extensions.Http per l'app autonoma Blazor WebAssembly o per l'app Client di una soluzione Blazor WebAssembly ospitata.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Nella portale di Azure concedere autorizzazioni delegate (ambiti)† per i dati di Microsoft Graph a cui l'app deve poter accedere per conto di un utente. Per l'esempio in questo articolo, la registrazione dell'app deve includere l'autorizzazione delegata per leggere i dati utente (Microsoft.Graph>User.Read ambito nelle autorizzazioni API, Tipo: Delegato). L'ambito User.Read consente agli utenti di accedere all'app e consente all'app di leggere il profilo e le informazioni aziendali degli utenti connessi. Per altre informazioni, vedere Panoramica delle autorizzazioni e del consenso in Microsoft Identity Platform e panoramica delle autorizzazioni di Microsoft Graph.

†Autorizzazioni e ambiti significano la stessa cosa e sono usati in modo intercambiabile nella documentazione sulla sicurezza e nel portale di Azure. A meno che il testo non faccia riferimento al portale di Azure, questo articolo usa scope/scopes quando si riferisce alle autorizzazioni di Graph.

Gli scope non distinguono tra maiuscole e minuscole, quindi User.Read è uguale a user.read. È possibile usare entrambi i formati, ma è consigliabile scegliere in modo coerente il codice dell'applicazione.

Dopo aver aggiunto gli ambiti dell'API Microsoft Graph alla registrazione dell'app nel portale di Azure, aggiungere al file wwwroot/appsettings.json dell'app la seguente configurazione delle impostazioni dell'app, che include l'URL di base di Graph insieme alla versione di Microsoft Graph e agli ambiti. Nell'esempio seguente viene specificato l'ambito User.Read per gli esempi nelle sezioni successive di questo articolo. Gli ambiti non fanno distinzione tra maiuscole e minuscole.

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

Nell'esempio precedente il {VERSION} segnaposto è la versione dell'API Microsoft Graph (ad esempio: v1.0).

Di seguito è riportato un esempio di file di configurazione completo wwwroot/appsettings.json per un'app che usa ME-ID come provider di identità, in cui la lettura dei dati utente (user.read ambito) viene specificata per 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"
    ]
  }
}

Nell'esempio precedente il {TENANT ID} segnaposto è l'ID directory (tenant) e il {CLIENT ID} segnaposto è l'ID applicazione (client). Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Crea la seguente GraphAuthorizationMessageHandler classe e la configurazione del progetto nel file Program per lavorare con l'API Graph. L'URL di base e gli ambiti vengono forniti al gestore dalla configurazione.

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" ]);
    }
}

È necessaria la barra finale (/) dell'URL autorizzato. Il codice precedente genera il seguente URL autorizzato in base alla configurazione delle impostazioni dell'app oppure utilizza il seguente URL autorizzato predefinito se la configurazione delle impostazioni dell'app non è presente: https://graph.microsoft.com/v1.0/.

Nel file Program, configurare l'elemento denominato HttpClient per API Graph:

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>();

Nell'esempio precedente l'oggetto GraphAuthorizationMessageHandlerDelegatingHandler viene registrato come servizio temporaneo per AddHttpMessageHandler. Si consiglia la registrazione transient per IHttpClientFactory, che gestisce autonomamente i propri ambiti DI. Per ulteriori informazioni, vedi le seguenti risorse:

È necessaria una barra finale (/) sull'indirizzo di base. Nel codice precedente, il terzo argomento di string.Join è string.Empty per garantire che la barra finale sia presente: https://graph.microsoft.com/v1.0/.

Chiamare l'API Graph da un componente usando un denominato HttpClient

La UserInfo.cs classe designa le proprietà del profilo utente necessarie con l'attributo JsonPropertyNameAttribute e il nome JSON usato da ME-ID. Nell'esempio seguente vengono impostate le proprietà per il numero di telefono cellulare e la posizione dell'ufficio dell'utente.

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; }
}

Nel componente seguente UserData viene creato un oggetto HttpClient per l'API Graph per emettere una richiesta per i dati del profilo dell'utente. La me risorsa (me) viene aggiunta all'URL di base con la versione per la richiesta dell'API Graph. I dati JSON restituiti da Graph vengono deserializzati nelle proprietà della UserInfo classe. Nell'esempio seguente viene ottenuto il numero di telefono cellulare. È possibile aggiungere codice simile per includere la posizione dell'ufficio del profilo ME-ID dell'utente, se lo si desidera (userInfo.OfficeLocation). Se la richiesta di token di accesso non riesce, l'utente viene reindirizzato per accedere all'app per un nuovo token di accesso.

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();
        }
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Suggerimento

Per aggiungere utenti a un'app, vedere la sezione Assegnare utenti a una registrazione dell'app con o senza ruoli dell'app.

La sequenza seguente descrive il nuovo flusso utente per gli ambiti dell'API Graph:

  1. Il nuovo utente accede all'app per la prima volta.
  2. L'utente acconsente all'uso dell'app nell'interfaccia utente del consenso di Azure.
  3. L'utente accede a una pagina del componente che richiede i dati dell'API Graph per la prima volta.
  4. L'utente viene reindirizzato all'interfaccia utente di consenso di Azure per fornire il consenso agli ambiti dell'API Graph.
  5. Vengono restituiti i dati utente dell'API Graph.

Se si preferisce che il provisioning degli ambiti (consenso per gli ambiti di API Graph) avvenga al primo accesso, specificare gli ambiti per l'autenticazione MSAL come ambiti predefiniti del token di accesso nel file 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);
+   }
});

Importante

Vedere la sezione DefaultAccessTokenScopes rispetto a AdditionalScopesToConsent per una spiegazione del motivo per cui il codice precedente usa per aggiungere gli ambiti anziché .

Quando vengono apportate le modifiche precedenti all'app, il flusso utente adotta la sequenza seguente:

  1. Il nuovo utente accede all'app per la prima volta.
  2. L'utente acconsente all'uso dell'app e degli ambiti dell'API Graph nell'interfaccia utente del consenso di Azure.
  3. L'utente accede a una pagina del componente che richiede i dati dell'API Graph per la prima volta.
  4. Vengono restituiti i dati utente dell'API Graph.

Quando si esegue il test con l'API Graph in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Personalizzare le attestazioni utente usando un nome HttpClient

Nell'esempio seguente l'app crea le attestazioni relative al numero di telefono cellulare e alla posizione dell'ufficio per l'utente dai dati del profilo utente ME-ID. L'app deve avere configurato in ME-ID l'ambito dell'API User.Read Graph. Gli account utente di test in ME-ID richiedono l'inserimento del numero di cellulare e della sede dell'ufficio, che possono essere aggiunti nei relativi profili utente tramite il portale Azure.

Se non è già stata aggiunta la UserInfo classe all'app seguendo le indicazioni riportate in precedenza in questo articolo, aggiungere la classe seguente e designare le proprietà del profilo utente necessarie con l'attributo JsonPropertyNameAttribute e il nome JSON usato da ME-ID. Nell'esempio seguente vengono impostate le proprietà per il numero di telefono cellulare e la posizione dell'ufficio dell'utente.

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; }
}

Nella seguente factory personalizzata dell'account utente:

  • Un ILogger (logger) è incluso per comodità, qualora si desideri registrare informazioni o errori nel metodo CreateUserAsync.
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al provider di identità per accedere al proprio account. È possibile eseguire azioni aggiuntive o diverse quando la richiesta di un token di accesso non riesce. Ad esempio, l'app può registrare un AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori verifiche.
  • Il RemoteUserAccount del framework rappresenta l'account dell'utente. Se l'app richiede una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata per RemoteUserAccount nel codice seguente.

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'autenticazione MSAL è configurata per utilizzare la factory personalizzata dell'account utente. Inizia confermando che il file Program utilizzi lo spazio dei nomi Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Nel file Program, individuare la chiamata al metodo di estensione AddMsalAuthentication. Aggiornare il codice come segue, includendo una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory del principal delle attestazioni dell'account con CustomAccountFactory.

Se l'app usa una classe di account utente personalizzata che estende RemoteUserAccount, scambiare la classe di account utente personalizzata dell'app per RemoteUserAccount nel codice seguente.

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

L'esempio precedente è relativo a un'app che usa l'autenticazione ME-ID con MSAL. Esistono modelli simili per l'autenticazione OIDC e API. Per altre informazioni, vedere gli esempi nella sezione Personalizzare l'utente con un claim del payload dell'articolo Scenari di sicurezza aggiuntivi di ASP.NET Core Blazor WebAssembly.

È possibile usare il componente seguente UserClaims per studiare le attestazioni dell'utente dopo l'autenticazione dell'utente con 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;
    }
}

Aggiungere un collegamento alla pagina del componente nel NavMenu componente (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>

Quando si esegue il test con l'API Graph in locale, è consigliabile usare una nuova sessione del browser InPrivate/incognito per ogni test per impedire che i cookie persistenti interferiscano con i test. Per altre informazioni, vedere Proteggere un'app autonoma di ASP.NET Core Blazor WebAssembly con Microsoft Entra ID.

Assegnare utenti a una registrazione di un'app con o senza ruoli dell'app

È possibile aggiungere utenti a una registrazione dell'app e assegnare ruoli agli utenti seguendo questa procedura nella portale di Azure.

Per aggiungere un utente, selezionare Utenti nell'area ME-ID del portale di Azure:

  1. Selezionare Nuovo utente>Crea nuovo utente.
  2. Usare il modello Crea utente .
  3. Fornire le informazioni dell'utente nell'area Identity .
  4. È possibile generare una password iniziale o assegnare una password iniziale modificata dall'utente al primo accesso. Se si usa la password generata dal portale, prendere nota di questa password.
  5. Selezionare Crea per creare l'utente. Quando crea nuova interfaccia utente si chiude, selezionare Aggiorna per aggiornare l'elenco utenti e visualizzare il nuovo utente.
  6. Per gli esempi in questo articolo, assegnare un numero di telefono cellulare al nuovo utente selezionando il nome dall'elenco degli utenti, selezionando Proprietà e modificando le informazioni di contatto per fornire un numero di telefono cellulare.

Per assegnare utenti all'app senza ruoli dell'app:

  1. Nell'area ME-ID del portale di Azure aprire Applicazioni aziendali.
  2. Selezionare l'app dall'elenco.
  3. Seleziona Utenti e gruppi.
  4. Selezionare Aggiungi utente/gruppo.
  5. Seleziona un utente.
  6. Selezionare il pulsante Assegna.

Per assegnare gli utenti all'app tramite i ruoli dell'app:

  1. Aggiungere ruoli alla registrazione dell'app nel portale di Azure seguendo le indicazioni riportate in ASP.NET Core Blazor WebAssembly con i gruppi e i ruoli di Microsoft Entra ID.
  2. Nell'area ME-ID del portale di Azure aprire Applicazioni aziendali.
  3. Selezionare l'app dall'elenco.
  4. Seleziona Utenti e gruppi.
  5. Selezionare Aggiungi utente/gruppo.
  6. Selezionare un utente e selezionare il proprio ruolo per l'accesso all'app. Più ruoli vengono assegnati a un utente ripetendo il processo di aggiunta dell'utente all'app fino a quando non vengono assegnati tutti i ruoli per un utente. Gli utenti con più ruoli vengono elencati una volta per ogni ruolo assegnato nell'elenco Utenti e gruppi di utenti per l'app.
  7. Selezionare il pulsante Assegna.

DefaultAccessTokenScopes rispetto a AdditionalScopesToConsent

Gli esempi in questo articolo effettuano il provisioning degli ambiti dell'API Graph con DefaultAccessTokenScopes, non AdditionalScopesToConsent.

AdditionalScopesToConsent non viene usato perché non è in grado di effettuare il provisioning degli ambiti dell'API Graph per gli utenti quando accedono all'app per la prima volta con MSAL tramite l'interfaccia utente del consenso di Azure. Quando l'utente tenta di accedere all'API Graph per la prima volta con Graph SDK, viene confrontato con un'eccezione:

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

Dopo che un utente effettua il provisioning degli ambiti dell'API Graph forniti tramite DefaultAccessTokenScopes, l'app può usare AdditionalScopesToConsent per un accesso utente successivo. Tuttavia, la modifica del codice dell'app non ha senso per un'app di produzione che richiede l'aggiunta periodica di nuovi utenti con ambiti Graph delegati o l'aggiunta di nuovi ambiti dell'API Graph delegata all'app.

La discussione precedente su come effettuare il provisioning degli ambiti per l'accesso all'API Graph quando l'utente accede per la prima volta all'app si applica solo a:

  • App che adottano Graph SDK.
  • App che usano HttpClient denominato per l'accesso all'API Graph e che chiedono agli utenti di acconsentire agli ambiti di Graph al primo accesso all'applicazione.

Quando si usa un elemento denominato HttpClient che non chiede agli utenti di prestare il consenso agli scope di Graph al primo accesso, gli utenti vengono reindirizzati all'interfaccia di consenso di Azure per il consenso agli scope dell'API Graph quando richiedono per la prima volta l'accesso all'API Graph tramite il DelegatingHandler preconfigurato e denominato HttpClient. Quando gli ambiti Graph non vengono concessi inizialmente con l'approccio denominato HttpClient, né DefaultAccessTokenScopes né AdditionalScopesToConsent vengono richiamati dall'app. Per altre informazioni, vedere la copertura denominata HttpClient in questo articolo.

Soluzioni ospitate Blazor WebAssembly

Gli esempi in questo articolo riguardano l'uso di Graph SDK o un oggetto denominato HttpClient con l'API Graph direttamente da un'app autonoma Blazor WebAssembly o direttamente dall'app Client di una soluzione ospitataBlazor WebAssembly. Uno scenario aggiuntivo non trattato in questo articolo riguarda un'app Client di una soluzione ospitata per chiamare l'app Server della soluzione tramite l'API Web e quindi l'app Server usa Graph SDK/API per chiamare Microsoft Graph e restituire i dati all'app Client . Anche se si tratta di un approccio supportato, non è trattato in questo articolo. Se si vuole adottare questo approccio:

  • Seguire le indicazioni in Chiamare un'API Web da un'app Blazor ASP.NET Core per gli aspetti relativi all'API Web riguardanti l'invio di richieste all'app Server dall'app Client e la restituzione dei dati all'app Client.
  • Seguire le indicazioni nella documentazione principale di Microsoft Graph per usare Graph SDK con un'app tipica ASP.NET Core, che in questo scenario è l'app Server della soluzione. Se si usa il Blazor WebAssembly modello di progetto per creare la soluzione ospitata Blazor WebAssembly (ASP.NET Core Hosted/-h|--hosted) con autorizzazione organizzativa (singola organizzazione/SingleOrg o più organizzazioni/MultiOrg) e l'opzione Microsoft Graph (Microsoft Identity Platform>Connected Services>Aggiungere le autorizzazioni di Microsoft Graph in Visual Studio o l'opzione --calls-graph con il comando .NET CLI dotnet new), l'app della soluzione è configurata per l'uso di Graph SDK quando la soluzione viene creata dal modello di progetto.

Risorse aggiuntive

Indicazioni generali

  • Documentazione di Microsoft Graph
  • App di esempio di Microsoft Graph Blazor WebAssembly: questo esempio illustra come usare l'SDK .NET di Microsoft Graph per accedere ai dati in Office 365 nelle app Blazor WebAssembly.
  • Esercitazione su come creare app .NET con Microsoft Graph e app di esempio ASP.NET Core per Microsoft Graph: queste risorse sono più appropriate per le soluzioni ospitateBlazor WebAssembly, in cui l'app è configurata per accedere a Microsoft Graph come una tipica app ASP.NET Core per conto dell'app . L'app Client usa l'API web per effettuare richieste all'app Server per i dati di Graph. Sebbene queste risorse non si applichino direttamente alla chiamata di Graph da app lato clientBlazor WebAssembly, la configurazione dell'app ME-ID e le pratiche di codifica di Microsoft Graph nelle risorse collegate sono pertinenti anche per le app autonome Blazor WebAssembly e dovrebbero essere consultate come riferimento per le linee guida generali.

Indicazioni sulla sicurezza