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 8 di questo articolo.

Avviso

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

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 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.

  • Denominato HttpClient con l'API Graph: un oggetto denominato HttpClient può inviare richieste API Microsoft Graph direttamente a Microsoft Graph. Selezionare il pulsante Named HttpClient with Graph API (HttpClient denominato con 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 si sta aprendo un problema per l'esempio di Azure, fornire un collegamento all'esempio nel commento di apertura perché il repository di esempio di Azure (Azure-Samples) contiene molti esempi. 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 commenti e suggerimenti o richiedere assistenza per questo articolo o ASP.NET Core, vedere nozioni fondamentali su ASP.NET CoreBlazor.

Importante

Gli scenari descritti in questo articolo si applicano all'uso di Microsoft Entra (ME-ID) come identity provider, non AAD B2C. L'uso di Microsoft Graph con un'app lato Blazor WebAssembly client e il provider AAD B2C identity non è attualmente supportato perché l'app richiederebbe un segreto client, che non può essere protetto nell'app lato Blazor client. Per un'app autonoma Blazor WebAssembly AAD B2C usare l'API Graph, creare un'API del server back-end (Web) per accedere all'API Graph per conto degli utenti. L'app sul lato client autentica e autorizza gli utenti a chiamare l'API Web per accedere in modo sicuro a Microsoft Graph e restituire i dati all'app lato Blazor client dall'API Web basata su server. 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 client in un'app lato Blazor client.

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 altre informazioni, vedere la sezione Soluzioni ospitate Blazor WebAssembly 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.

Microsoft Graph SDK per l'uso nelle Blazor app è denominato libreria client .NET di Microsoft Graph.

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 nella piattaforma Microsoft identity e Panoramica delle autorizzazioni di Microsoft Graph.

†Permissions e ambiti indicano la stessa cosa e vengono usati in modo intercambiabile nella documentazione di sicurezza e nella portale di Azure. A meno che il testo non faccia riferimento alla portale di Azure, questo articolo usa ambiti di ambito/ quando si fa riferimento alle autorizzazioni 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 gli ambiti dell'API Microsoft Graph alla registrazione dell'app nella portale di Azure, aggiungere la configurazione delle impostazioni dell'app seguente al wwwroot/appsettings.json file nell'app, che include l'URL di base graph con la versione e gli ambiti di Microsoft Graph. 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 identity provider, in cui viene specificata la lettura dei dati utente (user.read ambito) 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 forniti alla Scopes proprietà di AccessTokenRequestOptions nel AuthenticateRequestAsync metodo .

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

Quando non viene ottenuto un token di accesso, il codice seguente non imposta un'intestazione di autorizzazione Bearer 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

Per una spiegazione sul motivo per cui il codice precedente usa DefaultAccessTokenScopes per aggiungere gli ambiti anziché AdditionalScopesToConsent.DefaultAccessTokenScopes AdditionalScopesToConsent

Program Nel file aggiungere i servizi client Graph e la configurazione con il AddGraphClient metodo di estensione. Per impostazione predefinita, il codice seguente è l'indirizzo di base e User.Read gli ambiti di Microsoft Graph versione 1.0 se queste impostazioni non vengono trovate nel file delle 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, 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 factory dell'account utente personalizzata seguente:

  • Un ILogger oggetto (logger) è incluso per praticità nel caso in cui si desideri registrare informazioni o errori nel CreateUserAsync metodo .
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al identity provider 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 AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori indagini.
  • Il framework rappresenta l'account RemoteUserAccount 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 l'uso della factory dell'account utente personalizzata.

Verificare che il Program file usi lo Microsoft.AspNetCore.Components.WebAssembly.Authentication spazio dei nomi :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'esempio in questa sezione si basa sull'approccio di lettura dell'URL di base con la versione e gli ambiti dalla configurazione dell'app tramite la MicrosoftGraph sezione nel wwwroot/appsettings.json file. 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);

Program Nel file trovare la chiamata al AddMsalAuthentication metodo di estensione. Aggiornare il codice al codice seguente, che include una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory dell'entità 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, vedere la guida al log delle modifiche e all'aggiornamento di Microsoft Graph .NET SDK v5.

Microsoft Graph SDK per l'uso nelle Blazor app è denominato libreria client .NET di Microsoft Graph.

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 nella piattaforma Microsoft identity e Panoramica delle autorizzazioni di Microsoft Graph.

†Permissions e ambiti indicano la stessa cosa e vengono usati in modo intercambiabile nella documentazione di sicurezza e nella portale di Azure. A meno che il testo non faccia riferimento alla portale di Azure, questo articolo usa ambiti di ambito/ quando si fa riferimento alle autorizzazioni 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 gli ambiti dell'API Microsoft Graph alla registrazione dell'app nella portale di Azure, aggiungere la configurazione delle impostazioni dell'app seguente al wwwroot/appsettings.json file nell'app, che include l'URL di base graph con la versione e gli ambiti di Microsoft Graph. 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 identity provider, in cui viene specificata la lettura dei dati utente (user.read ambito) 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 forniti alla Scopes proprietà di AccessTokenRequestOptions nel AuthenticateRequestAsync metodo . Viene IHttpProvider.OverallTimeout esteso dal valore predefinito da 100 secondi a 300 secondi per dare più HttpClient tempo per ricevere una risposta da Microsoft Graph.

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

Quando non viene ottenuto un token di accesso, il codice seguente non imposta un'intestazione di autorizzazione Bearer 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

Per una spiegazione sul motivo per cui il codice precedente usa DefaultAccessTokenScopes per aggiungere gli ambiti anziché AdditionalScopesToConsent.DefaultAccessTokenScopes AdditionalScopesToConsent

Program Nel file aggiungere i servizi client Graph e la configurazione con il metodo di AddGraphClient estensione:

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 factory dell'account utente personalizzata seguente:

  • Un ILogger oggetto (logger) è incluso per praticità nel caso in cui si desideri registrare informazioni o errori nel CreateUserAsync metodo .
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al identity provider 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 AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori indagini.
  • Il framework rappresenta l'account RemoteUserAccount 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 l'uso della factory dell'account utente personalizzata.

Verificare che il Program file usi lo Microsoft.AspNetCore.Components.WebAssembly.Authentication spazio dei nomi :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

L'esempio in questa sezione si basa sull'approccio di lettura dell'URL di base con la versione e gli ambiti dalla configurazione dell'app tramite la MicrosoftGraph sezione nel wwwroot/appsettings.json file. 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);

Program Nel file trovare la chiamata al AddMsalAuthentication metodo di estensione. Aggiornare il codice al codice seguente, che include una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory dell'entità 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 per Microsoft.Extensions.Http l'app autonoma Blazor WebAssembly .

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

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 nella piattaforma Microsoft identity e Panoramica delle autorizzazioni di Microsoft Graph.

†Permissions e ambiti indicano la stessa cosa e vengono usati in modo intercambiabile nella documentazione di sicurezza e nella portale di Azure. A meno che il testo non faccia riferimento alla portale di Azure, questo articolo usa ambiti di ambito/ quando si fa riferimento alle autorizzazioni 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 gli ambiti dell'API Microsoft Graph alla registrazione dell'app nella portale di Azure, aggiungere la configurazione delle impostazioni dell'app seguente al wwwroot/appsettings.json file nell'app, che include l'URL di base graph con la versione e gli ambiti di Microsoft Graph. 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 identity provider, in cui viene specificata la lettura dei dati utente (user.read ambito) 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.

Creare la classe e la configurazione del progetto seguenti GraphAuthorizationMessageHandler nel file per l'uso Program dell'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 compila l'URL autorizzato seguente dalla configurazione delle impostazioni dell'app o usa l'URL autorizzato seguente se la configurazione delle impostazioni dell'app è mancante: https://graph.microsoft.com/v1.0/.

Program Nel file configurare l'API Graph denominataHttpClient:

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 GraphAuthorizationMessageHandler DelegatingHandler viene registrato come servizio temporaneo per AddHttpMessageHandler. La registrazione temporanea è consigliata per IHttpClientFactory, che gestisce i propri ambiti di inserimento delle dipendenze. Per ulteriori informazioni, vedi le seguenti risorse:

È necessaria una barra finale (/) sull'indirizzo di base. Nel codice precedente, il terzo argomento di string.Join consiste string.Empty nel 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 dell'ambito (consenso per gli ambiti dell'API Graph) venga eseguito nell'accesso iniziale, fornire gli ambiti all'autenticazione MSAL come ambiti predefiniti del token di accesso nel Program file:

+ 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

Per una spiegazione sul motivo per cui il codice precedente usa DefaultAccessTokenScopes per aggiungere gli ambiti anziché AdditionalScopesToConsent.DefaultAccessTokenScopes AdditionalScopesToConsent

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 l'ambito dell'API User.Read Graph configurato in ME-ID. Gli account utente di test in ME-ID richiedono una voce per il numero di telefono cellulare e la posizione dell'ufficio, che possono essere aggiunti tramite il portale di Azure ai propri profili utente.

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 factory dell'account utente personalizzata seguente:

  • Un ILogger oggetto (logger) è incluso per praticità nel caso in cui si desideri registrare informazioni o errori nel CreateUserAsync metodo .
  • Nel caso in cui venga generata un'eccezione AccessTokenNotAvailableException , l'utente viene reindirizzato al identity provider 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 AccessTokenNotAvailableException e creare un ticket di supporto per ulteriori indagini.
  • Il framework rappresenta l'account RemoteUserAccount 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 l'uso della factory dell'account utente personalizzata. Per iniziare, verificare che il Program file usi lo Microsoft.AspNetCore.Components.WebAssembly.Authentication spazio dei nomi :

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Program Nel file trovare la chiamata al AddMsalAuthentication metodo di estensione. Aggiornare il codice al codice seguente, che include una chiamata a AddAccountClaimsPrincipalFactory che aggiunge una factory dell'entità 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'attestazione di payload dell'articolo scenari di sicurezza aggiuntivi di ASP.NET CoreBlazor 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 dell'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 utenti all'app con 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 e 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 un accesso denominato HttpClient per l'API Graph che chiede agli utenti di fornire il consenso agli ambiti graph al primo accesso all'app.

Quando si usa un oggetto denominato HttpClient che non chiede agli utenti di fornire il consenso agli ambiti graph nel primo accesso, gli utenti vengono reindirizzati all'interfaccia utente di consenso di Azure per gli ambiti dell'API Graph quando richiedono per la prima volta l'accesso all'API Graph tramite il DelegatingHandler nome del preconfigurato.HttpClient Quando gli ambiti di Graph non vengono concessi inizialmente con l'approccio denominato HttpClient , né DefaultAccessTokenScopes AdditionalScopesToConsent vengono chiamati 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 riportate in Chiamare un'API Web da un'app ASP.NET Core Blazor per gli aspetti dell'API Web sull'emissione di richieste all'app Server dall'app Client e sulla restituzione dei dati all'appClient.
  • 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 (Piattaforma Microsoft identity>Connected Services>Aggiungere autorizzazioni Microsoft Graph in Visual Studio o l'opzione --calls-graph con il comando dell'interfaccia della riga di comando di .NETdotnet new), il Server 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 GraphBlazor WebAssembly: questo esempio illustra come usare Microsoft Graph .NET SDK per accedere ai dati in Office 365 dalle Blazor WebAssembly app.
  • Creare app .NET con l'esercitazione su Microsoft Graph e l'esempio di Microsoft Graph ASP.NET'app Core: anche se queste risorse non si applicano direttamente a Graph dalle app latoBlazor WebAssembly client, la configurazione dell'app ME-ID e le procedure di codifica di Microsoft Graph nelle risorse collegate sono rilevanti per le app autonome Blazor WebAssembly e devono essere consultate per le procedure consigliate generali.
  • Documentazione di Microsoft Graph
  • App di esempio di Microsoft GraphBlazor WebAssembly: questo esempio illustra come usare Microsoft Graph .NET SDK per accedere ai dati in Office 365 dalle Blazor WebAssembly app.
  • Creare app .NET con l'esercitazione su Microsoft Graph e l'esempio di Microsoft Graph ASP.NET'app Core: queste risorse sono più appropriate per le soluzioni ospitateBlazor WebAssembly, in cui l'app Server è configurata per accedere a Microsoft Graph come un'app ASP.NET Core tipica per conto dell'app.Client L'app Client usa l'API Web per effettuare richieste all'app per i Server dati graph. Anche se queste risorse non si applicano direttamente a Graph dalle app latoBlazor WebAssembly client, la configurazione dell'app ME-ID e le procedure di codifica di Microsoft Graph nelle risorse collegate sono rilevanti per le app autonome Blazor WebAssembly e devono essere consultate per le procedure consigliate generali.

Indicazioni sulla sicurezza