Partilhar via


Usar a API do Graph com o ASP.NET Core Blazor WebAssembly

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

Este artigo explica como usar o Microsoft Graph em aplicativos Blazor WebAssembly, o que permite que os aplicativos acessem os recursos do Microsoft Cloud.

Duas abordagens são adotadas:

  • SDK do Graph: o SDK do Microsoft Graph simplifica a criação de aplicativos de alta qualidade, eficientes e resilientes que acessam o Microsoft Graph. Selecione o botão SDK do Graph na parte superior deste artigo para adotar essa abordagem.

  • HttpClient nomeado com a API do Graph: um HttpClient nomeado pode emitir solicitações da API do Microsoft Graph diretamente no Graph. Selecione o botão HttpClient nomeado com a API do Graph na parte superior deste artigo para adotar essa abordagem.

As diretrizes neste artigo não se destinam a substituir a documentação do Microsoft Graph e as diretrizes de segurança do Azure em outros conjuntos de documentação da Microsoft. Avalie as diretrizes de segurança na seção Recursos adicionais deste artigo antes de implementar o Microsoft Graph em um ambiente de produção. Siga todas as práticas recomendadas da Microsoft as vulnerabilidades de seus aplicativos.

Abordagens adicionais para trabalhar com o Microsoft Graph e o Blazor WebAssembly são fornecidas pelos seguintes exemplos do Microsoft Graph e do Azure:

Para fornecer comentários sobre qualquer um dos dois exemplos anteriores, abra um problema no repositório GitHub do exemplo. Se você estiver abrindo um problema para o exemplo do Azure, forneça um link para o exemplo no comentário de abertura, pois o repositório de exemplo do Azure (Azure-Samples) contém muitos exemplos. Descreva o problema em detalhes e inclua o código de exemplo conforme necessário. Coloque um aplicativo mínimo no GitHub que reproduza o problema ou erro. Certifique-se de remover os dados de configuração da conta do Azure do exemplo antes de confirmá-lo no repositório público.

Para fornecer comentários ou buscar ajuda com este artigo ou o ASP.NET Core, consulte Princípios básicos do ASP.NET Core Blazor.

Importante

Os cenários descritos neste artigo se aplicam ao uso do ME-ID (Microsoft Entra) como o provedor de identity, não ao AAD B2C. O uso do Microsoft Graph com um aplicativo Blazor WebAssembly do lado do cliente e o provedor de identity AAD B2C não tem suporte no momento porque o aplicativo exigiria um segredo do cliente, que não pode ser protegido no aplicativo de Blazor do lado do cliente. Para um aplicativo Blazor WebAssembly autônomo do AAD B2C, use a API do Graph, crie uma API de servidor de back-end (Web) para acessar a API do Graph em nome dos usuários. O aplicativo do lado do cliente autentica e autoriza os usuários a chamar a API Web para acessar com segurança o Microsoft Graph e retornar dados para o aplicativo Blazor do lado do cliente a partir da sua API Web do lado do servidor. O segredo do cliente é mantido com segurança na API Web baseada no servidor, não no aplicativo Blazor no cliente. Nunca armazene um segredo de cliente em um aplicativo Blazor do lado do cliente.

Há suporte para o uso de um aplicativo hospedado Blazor WebAssembly, em que o aplicativo Server usa o SDK/API do Graph para fornecer dados do Graph ao aplicativo Client por meio da API Web. Para saber mais, consulte a seção Soluções Blazor WebAssembly hospedadas deste artigo.

Os exemplos deste artigo aproveitam os novos recursos do .NET/C#. Quando você usar os exemplos com o .NET 7 ou versões anteriores, pequenas modificações serão necessárias. No entanto, os exemplos de texto e código que pertencem à interação com o Microsoft Graph são os mesmos para todas as versões do ASP.NET Core.

As diretrizes a seguir se aplicam ao Microsoft Graph v5.

O SDK do Microsoft Graph para uso em aplicativos Blazor é chamado deBiblioteca de Clientes do .NET do Microsoft Graph.

Os exemplos do SDK do Graph exigem as seguintes referências de pacote no aplicativo Blazor WebAssembly autônomo. Os dois primeiros pacotes já serão referenciados se o aplicativo tiver sido habilitado para autenticação MSAL, por exemplo, ao criar o aplicativo seguindo as diretrizes em Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Os exemplos do SDK do Graph exigem as seguintes referências de pacote no aplicativo autônomo Blazor WebAssembly ou no aplicativo Client de uma solução hospedada Blazor WebAssembly. Os dois primeiros pacotes já serão referenciados se o aplicativo tiver sido habilitado para autenticação MSAL, por exemplo, ao criar o aplicativo seguindo as diretrizes em Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

No portal do Microsoft Azure, conceda permissões delegadas (escopos)† para os dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo deste artigo, o registro do aplicativo deve incluir a permissão delegada para leitura dos dados do usuário (escopo Microsoft.Graph>User.Read em permissões de API, Tipo: Delegado). O escopo User.Read permite que os usuários entrem no aplicativo e permite que o aplicativo faça a leitura do perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral das permissões e consentimento na plataforma de identity da Microsoft e Visão geral das permissões do Microsoft Graph.

Permissões e escopos significam a mesma coisa e são usados de forma intercambiável na documentação de segurança e no portal do Microsoft Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao se referir a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto, User.Read e user.read são a mesma coisa. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente no código do aplicativo.

Depois de adicionar os escopos da API do Microsoft Graph ao registro do aplicativo no portal do Microsoft Azure, adicione a seguinte configuração de definições do aplicativo ao arquivo wwwroot/appsettings.json no aplicativo, que inclui a URL de base do Graph com a versão e os escopos do Microsoft Graph. No exemplo a seguir, o escopo User.Read é especificado para os exemplos em seções posteriores deste artigo. Os escopos não diferenciam maiúsculas de minúsculas.

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

No exemplo anterior, o espaço reservado {VERSION} é a versão da API do Microsoft Graph (por exemplo: v1.0).

A seguir, um exemplo de um arquivo de configuração wwwroot/appsettings.json completo para um aplicativo que usa ME-ID como seu provedor de identity, em que a leitura de dados do usuário (escopo user.read) é especificada para o 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"
    ]
  }
}

No exemplo anterior, o espaço reservado {TENANT ID} é a ID do Diretório (locatário) e o espaço reservado {CLIENT ID} é a ID do Aplicativo (cliente). Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Adicione a classe GraphClientExtensions a seguir ao aplicativo autônomo. Os escopos são fornecidos para a propriedade Scopes do AccessTokenRequestOptions no método AuthenticateRequestAsync.

Adicione a seguinte classe GraphClientExtensions ao aplicativo autônomo ou aplicativo Client de uma Blazor WebAssemblysolução hospedada. Os escopos são fornecidos para a propriedade Scopes do AccessTokenRequestOptions no método AuthenticateRequestAsync.

Quando um token de acesso não é obtido, o código a seguir não define um cabeçalho de autorização do Portador para solicitações do 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

Consulte a seção DefaultAccessTokenScopes versus AdditionalScopesToConsent para obter uma explicação sobre por que o código anterior usa DefaultAccessTokenScopes para adicionar os escopos em vez de AdditionalScopesToConsent.

No arquivo Program, adicione os serviços de cliente do Graph e a configuração com o método de extensão AddGraphClient. O código a seguir será padronizado para o endereço básico e para os escopos User.Read do Microsoft Graph versão 1.0 se essas configurações não forem encontradas no arquivo de configurações do aplicativo:

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

Chamar API do Graph de um componente usando o SDK do Graph

O componente UserData a seguir usa um GraphServiceClient injetado para obter os dados de perfil do ME-ID do usuário e exibir seu número de telefone celular.

Para qualquer usuário de teste criado no ME-ID, certifique-se de fornecer ao perfil do ME-ID do usuário um número de telefone celular no portal do 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();
    }
}

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Dica

Para adicionar usuários a um aplicativo, consulte a seção Atribuir usuários a um registro de aplicativo com ou sem funções de aplicativo.

Ao testar com o SDK do Graph localmente, recomendamos o uso de uma nova sessão do navegador InPrivate/anônimo para cada teste a fim de evitar que os cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Personalizar declarações de usuário usando o SDK do Graph

No exemplo a seguir, o aplicativo cria declarações de número de telefone celular e local do escritório para um usuário dos dados de seu perfil de usuário do ME-ID. O aplicativo deve ter o escopo da API do Graph User.Read configurado no ME-ID. Todos os usuários de teste para esse cenário devem ter um número de telefone celular e um local de escritório em seu perfil do ME-ID, que pode ser adicionado por meio do portal do Azure.

Na seguinte fábrica de contas de usuário personalizada:

  • Um ILogger (logger) é incluído para conveniência caso você deseje registrar informações ou erros no método CreateUserAsync.
  • Caso um AccessTokenNotAvailableException seja gerado, o usuário será redirecionado para o provedor de identity para entrar em sua conta. Ações adicionais ou diferentes podem ser executadas ao solicitar um token de acesso falhar. O aplicativo, por exemplo, pode registrar o AccessTokenNotAvailableException e criar um tíquete de suporte para uma investigação mais aprofundada.
  • A RemoteUserAccount da estrutura representa a conta do usuário. Se o aplicativo exigir uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, troque sua classe de conta de usuário personalizada para RemoteUserAccount no código a seguir.

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

Configure a autenticação MSAL para usar o alocador de contas de usuário personalizado.

Confirme se o arquivo Program usa o namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

O exemplo nesta seção baseia-se na abordagem de ler a URL base com a versão e os escopos da configuração do aplicativo por meio da seção MicrosoftGraph no arquivo wwwroot/appsettings.json. Por você seguir as diretrizes anteriores deste artigo, as seguintes linhas já estarão presentes no arquivo Program:

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

No arquivo Program, encontre a chamada ao método de extensão AddMsalAuthentication. Atualize o código para o seguinte, que inclui uma chamada para AddAccountClaimsPrincipalFactory que adiciona uma fábrica de entidades de segurança de declarações de conta com o CustomAccountFactory.

Se o aplicativo usar uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, troque a classe de conta de usuário personalizada para RemoteUserAccount no código a seguir.

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

Você pode usar o seguinte componente UserClaims para estudar as declarações do usuário após a autenticação do usuário com o 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;
    }
}

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Ao testar com o SDK do Graph localmente, recomendamos o uso de uma nova sessão do navegador InPrivate/anônimo para cada teste a fim de evitar que os cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

As diretrizes a seguir se aplicam ao Microsoft Graph v4. Se você estiver atualizando um aplicativo do SDK v4 para o v5, consulte o guia de atualização e o log de alterações do SDK do .NET do Microsoft Graph v5.

O SDK do Microsoft Graph para uso em aplicativos Blazor é chamado deBiblioteca de Clientes do .NET do Microsoft Graph.

Os exemplos do SDK do Graph exigem as seguintes referências de pacote no aplicativo Blazor WebAssembly autônomo. Os dois primeiros pacotes já serão referenciados se o aplicativo tiver sido habilitado para autenticação MSAL, por exemplo, ao criar o aplicativo seguindo as diretrizes em Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Os exemplos do SDK do Graph exigem as seguintes referências de pacote no aplicativo autônomo Blazor WebAssembly ou no aplicativo Client de uma solução hospedada Blazor WebAssembly. Os dois primeiros pacotes já serão referenciados se o aplicativo tiver sido habilitado para autenticação MSAL, por exemplo, ao criar o aplicativo seguindo as diretrizes em Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

No portal do Microsoft Azure, conceda permissões delegadas (escopos)† para os dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo deste artigo, o registro do aplicativo deve incluir a permissão delegada para leitura dos dados do usuário (escopo Microsoft.Graph>User.Read em permissões de API, Tipo: Delegado). O escopo User.Read permite que os usuários entrem no aplicativo e permite que o aplicativo faça a leitura do perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral das permissões e consentimento na plataforma de identity da Microsoft e Visão geral das permissões do Microsoft Graph.

Permissões e escopos significam a mesma coisa e são usados de forma intercambiável na documentação de segurança e no portal do Microsoft Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao se referir a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto, User.Read e user.read são a mesma coisa. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente no código do aplicativo.

Depois de adicionar os escopos da API do Microsoft Graph ao registro do aplicativo no portal do Microsoft Azure, adicione a seguinte configuração de definições do aplicativo ao arquivo wwwroot/appsettings.json no aplicativo, que inclui a URL de base do Graph com a versão e os escopos do Microsoft Graph. No exemplo a seguir, o escopo User.Read é especificado para os exemplos em seções posteriores deste artigo. Os escopos não diferenciam maiúsculas de minúsculas.

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

No exemplo anterior, o espaço reservado {VERSION} é a versão da API do Microsoft Graph (por exemplo: v1.0).

A seguir, um exemplo de um arquivo de configuração wwwroot/appsettings.json completo para um aplicativo que usa ME-ID como seu provedor de identity, em que a leitura de dados do usuário (escopo user.read) é especificada para o 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"
    ]
  }
}

No exemplo anterior, o espaço reservado {TENANT ID} é a ID do Diretório (locatário) e o espaço reservado {CLIENT ID} é a ID do Aplicativo (cliente). Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Adicione a classe GraphClientExtensions a seguir ao aplicativo autônomo. Os escopos são fornecidos para a propriedade Scopes do AccessTokenRequestOptions no método AuthenticateRequestAsync. O IHttpProvider.OverallTimeout é estendido do valor padrão de 100 segundos para 300 segundos para dar ao HttpClient mais tempo para receber uma resposta do Microsoft Graph.

Adicione a seguinte classe GraphClientExtensions ao aplicativo autônomo ou aplicativo Client de uma Blazor WebAssemblysolução hospedada. Os escopos são fornecidos para a propriedade Scopes do AccessTokenRequestOptions no método AuthenticateRequestAsync. O IHttpProvider.OverallTimeout é estendido do valor padrão de 100 segundos para 300 segundos para dar ao HttpClient mais tempo para receber uma resposta do Microsoft Graph.

Quando um token de acesso não é obtido, o código a seguir não define um cabeçalho de autorização do Portador para solicitações do 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

Consulte a seção DefaultAccessTokenScopes versus AdditionalScopesToConsent para obter uma explicação sobre por que o código anterior usa DefaultAccessTokenScopes para adicionar os escopos em vez de AdditionalScopesToConsent.

No arquivo Program, adicione os serviços de cliente do Graph e a configuração com o método de extensão 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);

Chamar API do Graph de um componente usando o SDK do Graph

O componente UserData a seguir usa um GraphServiceClient injetado para obter os dados de perfil do ME-ID do usuário e exibir seu número de telefone celular. Para qualquer usuário de teste criado no ME-ID, certifique-se de fornecer ao perfil do ME-ID do usuário um número de telefone celular no portal do 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();
    }
}

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Dica

Para adicionar usuários a um aplicativo, consulte a seção Atribuir usuários a um registro de aplicativo com ou sem funções de aplicativo.

Ao testar com o SDK do Graph localmente, recomendamos o uso de uma nova sessão do navegador InPrivate/anônimo para cada teste a fim de evitar que os cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Personalizar declarações de usuário usando o SDK do Graph

No exemplo a seguir, o aplicativo cria declarações de número de telefone celular e local do escritório para um usuário dos dados de seu perfil de usuário do ME-ID. O aplicativo deve ter o escopo da API do Graph User.Read configurado no ME-ID. Todos os usuários de teste para esse cenário devem ter um número de telefone celular e um local de escritório em seu perfil do ME-ID, que pode ser adicionado por meio do portal do Azure.

Na seguinte fábrica de contas de usuário personalizada:

  • Um ILogger (logger) é incluído para conveniência caso você deseje registrar informações ou erros no método CreateUserAsync.
  • Caso um AccessTokenNotAvailableException seja gerado, o usuário será redirecionado para o provedor de identity para entrar em sua conta. Ações adicionais ou diferentes podem ser executadas ao solicitar um token de acesso falhar. O aplicativo, por exemplo, pode registrar o AccessTokenNotAvailableException e criar um tíquete de suporte para uma investigação mais aprofundada.
  • A RemoteUserAccount da estrutura representa a conta do usuário. Se o aplicativo exigir uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, troque sua classe de conta de usuário personalizada para RemoteUserAccount no código a seguir.

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

Configure a autenticação MSAL para usar o alocador de contas de usuário personalizado.

Confirme se o arquivo Program usa o namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

O exemplo nesta seção baseia-se na abordagem de ler a URL base com a versão e os escopos da configuração do aplicativo por meio da seção MicrosoftGraph no arquivo wwwroot/appsettings.json. Por você seguir as diretrizes anteriores deste artigo, as seguintes linhas já estarão presentes no arquivo Program:

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

No arquivo Program, encontre a chamada ao método de extensão AddMsalAuthentication. Atualize o código para o seguinte, que inclui uma chamada para AddAccountClaimsPrincipalFactory que adiciona uma fábrica de entidades de segurança de declarações de conta com o CustomAccountFactory.

Se o aplicativo usar uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, troque a classe de conta de usuário personalizada para RemoteUserAccount no código a seguir.

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

Você pode usar o seguinte componente UserClaims para estudar as declarações do usuário após a autenticação do usuário com o 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;
    }
}

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Ao testar com o SDK do Graph localmente, recomendamos o uso de uma nova sessão do navegador InPrivate/anônimo para cada teste a fim de evitar que os cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Os exemplos a seguir usam um HttpClient nomeado na API do Graph chamadas a fim de obter o número de telefone celular de um usuário para processar uma chamada, ou personalizar as declarações de um usuário para incluir uma declaração de número de telefone celular e uma declaração de localização do escritório.

Os exemplos exigem uma referência de pacote do Microsoft.Extensions.Http para o aplicativo Blazor WebAssembly autônomo.

Os exemplos exigem uma referência de pacote para Microsoft.Extensions.Http o aplicativo autônomo Blazor WebAssembly ou o aplicativo Client de uma solução Blazor WebAssembly hospedada.

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

No portal do Microsoft Azure, conceda permissões delegadas (escopos)† para os dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo deste artigo, o registro do aplicativo deve incluir a permissão delegada para leitura dos dados do usuário (escopo Microsoft.Graph>User.Read em permissões de API, Tipo: Delegado). O escopo User.Read permite que os usuários entrem no aplicativo e permite que o aplicativo faça a leitura do perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral das permissões e consentimento na plataforma de identity da Microsoft e Visão geral das permissões do Microsoft Graph.

Permissões e escopos significam a mesma coisa e são usados de forma intercambiável na documentação de segurança e no portal do Microsoft Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao se referir a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto, User.Read e user.read são a mesma coisa. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente no código do aplicativo.

Depois de adicionar os escopos da API do Microsoft Graph ao registro do aplicativo no portal do Microsoft Azure, adicione a seguinte configuração de definições do aplicativo ao arquivo wwwroot/appsettings.json no aplicativo, que inclui a URL de base do Graph com a versão e os escopos do Microsoft Graph. No exemplo a seguir, o escopo User.Read é especificado para os exemplos em seções posteriores deste artigo. Os escopos não diferenciam maiúsculas de minúsculas.

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

No exemplo anterior, o espaço reservado {VERSION} é a versão da API do Microsoft Graph (por exemplo: v1.0).

A seguir, um exemplo de um arquivo de configuração wwwroot/appsettings.json completo para um aplicativo que usa ME-ID como seu provedor de identity, em que a leitura de dados do usuário (escopo user.read) é especificada para o 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"
    ]
  }
}

No exemplo anterior, o espaço reservado {TENANT ID} é a ID do Diretório (locatário) e o espaço reservado {CLIENT ID} é a ID do Aplicativo (cliente). Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Crie a configuração de projeto e a classe GraphAuthorizationMessageHandler no arquivo Program para trabalhar com a API do Graph. A URL base e os escopos são fornecidos ao manipulador a partir da configuração.

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

A barra à direita (/) do URL autorizado é obrigatória. O código anterior cria a seguinte URL autorizada a partir da configuração das definições do aplicativo ou usa como padrão a seguinte URL autorizada se a configuração das definições do aplicativo estiver ausente: https://graph.microsoft.com/v1.0/.

No arquivo Program, configure o HttpClient nomeado para a API do 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>();

No exemplo anterior, GraphAuthorizationMessageHandler DelegatingHandler é registrado como um serviço transitório para AddHttpMessageHandler. O registro transitório é recomendado para IHttpClientFactory, que gerencia seus próprios escopos de DI. Para obter mais informações, consulte os seguintes recursos:

É necessária uma barra à direita (/) no endereço básico. No código anterior, o terceiro argumento para string.Join é string.Empty para garantir que a barra à direita esteja presente: https://graph.microsoft.com/v1.0/.

Chamar a API do Graph de um componente usando um HttpClient nomeado

A classe UserInfo.cs designa as propriedades de perfil de usuário necessárias com o atributo JsonPropertyNameAttribute e o nome JSON usado pelo ME-ID. O exemplo a seguir configura propriedades para o número de telefone celular e o local do escritório do usuário.

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

No componente UserData a seguir, é criada uma HttpClient na API do Graph para emitir uma solicitação nos dados de perfil do usuário. O recurso me (me) é adicionado à URL base com a versão para a solicitação da API do Graph. Dados JSON retornados pelo Graph são desserializados nas propriedades da classe UserInfo. No exemplo a seguir, o número de telefone celular é obtido. Você pode adicionar código semelhante para incluir o local do escritório de perfil do ME-ID do usuário, se desejar (userInfo.OfficeLocation). Se a solicitação de token de acesso falhar, o usuário será redirecionado para entrar no aplicativo em um novo token de acesso.

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

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Dica

Para adicionar usuários a um aplicativo, consulte a seção Atribuir usuários a um registro de aplicativo com ou sem funções de aplicativo.

A sequência a seguir descreve o novo fluxo de usuários para escopos da API do Graph:

  1. O novo usuário faz a entrada no aplicativo pela primeira vez.
  2. O usuário consente com o uso do aplicativo na interface de usuário de consentimento do Azure.
  3. O usuário acessa uma página de componente que solicita dados da API do Graph pela primeira vez.
  4. O usuário é redirecionado para a interface de usuário de consentimento do Azure para consentir com os escopos da API do Graph.
  5. Os dados do usuário da API do Graph são retornados.

Se você preferir que o provisionamento de escopo (consentimento para escopos da API do Graph) ocorra na entrada inicial, forneça os escopos para a autenticação MSAL como escopos de token de acesso padrão no arquivo 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

Consulte a seção DefaultAccessTokenScopes versus AdditionalScopesToConsent para obter uma explicação sobre por que o código anterior usa DefaultAccessTokenScopes para adicionar os escopos em vez de AdditionalScopesToConsent.

Quando as alterações anteriores são feitas no aplicativo, o fluxo do usuário adota a seguinte sequência:

  1. O novo usuário faz a entrada no aplicativo pela primeira vez.
  2. O usuário consente em usar o aplicativo e os escopos da API do Graph na interface de usuário de consentimento do Azure.
  3. O usuário acessa uma página de componente que solicita dados da API do Graph pela primeira vez.
  4. Os dados do usuário da API do Graph são retornados.

Ao testar com a API do Graph localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/anônimo para cada teste para evitar que os cookies persistentes interfiram no teste. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Personalizar declarações de usuário usando um HttpClient nomeado

No exemplo a seguir, o aplicativo cria o número de telefone celular e as declarações de localização do escritório para o usuário a partir dos dados do perfil de usuário do ME-ID. O aplicativo deve ter o escopo da API do Graph User.Read configurado no ME-ID. As contas de usuário de teste no ME-ID exigem uma entrada para o número de telefone celular e o local do escritório, que pode ser adicionado por meio do portal do Azure aos perfis de usuário.

Se você ainda não adicionou a classe UserInfo ao aplicativo seguindo as diretrizes anteriores neste artigo, adicione a classe a seguir e designe as propriedades de perfil de usuário necessárias com o atributo JsonPropertyNameAttribute e o nome JSON usados pelo ME-ID. O exemplo a seguir configura propriedades para o número de telefone celular e o local do escritório do usuário.

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

Na seguinte fábrica de contas de usuário personalizada:

  • Um ILogger (logger) é incluído para conveniência caso você deseje registrar informações ou erros no método CreateUserAsync.
  • Caso um AccessTokenNotAvailableException seja gerado, o usuário será redirecionado para o provedor de identity para entrar em sua conta. Ações adicionais ou diferentes podem ser executadas ao solicitar um token de acesso falhar. O aplicativo, por exemplo, pode registrar o AccessTokenNotAvailableException e criar um tíquete de suporte para uma investigação mais aprofundada.
  • A RemoteUserAccount da estrutura representa a conta do usuário. Se o aplicativo exigir uma classe de conta de usuário personalizada que se estenda RemoteUserAccount, troque a classe de conta de usuário personalizada no RemoteUserAccount código a seguir.

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

A autenticação MSAL está configurada para usar a fábrica de contas de usuário personalizada. Comece confirmando que o arquivo Program usa o namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

No arquivo Program, encontre a chamada ao método de extensão AddMsalAuthentication. Atualize o código para o seguinte, que inclui uma chamada para AddAccountClaimsPrincipalFactory que adiciona uma fábrica de entidades de segurança de declarações de conta com o CustomAccountFactory.

Se o aplicativo usar uma classe de conta de usuário personalizada que se estenda RemoteUserAccount, troque a classe de conta de usuário personalizada do aplicativo no código RemoteUserAccount a seguir.

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

O exemplo anterior é para um aplicativo que usa a autenticação do ME-ID com MSAL. Existem padrões semelhantes para autenticação de OIDC e API. Para obter mais informações, consulte os exemplos na seção Personalizar o usuário com uma seção de declaração do artigo Cenários de segurança adicional do ASP.NET CoreBlazor WebAssembly.

Você pode usar o seguinte componente UserClaims para estudar as declarações do usuário após a autenticação do usuário com o 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;
    }
}

Adicione um link à página do componente no componente NavMenu (Layout/NavMenu.razor):

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

Ao testar com a API do Graph localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/anônimo para cada teste para evitar que os cookies persistentes interfiram no teste. Para obter mais informações, consulte Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com contas do Microsoft Entra ID.

Atribuir usuários a um registro de aplicativo com ou sem funções de aplicativo

Você pode adicionar usuários a um registro de aplicativo e atribuir funções a usuários com as seguintes etapas no portal do Microsoft Azure.

Para adicionar um usuário, selecione Usuários na área ME-ID do portal do Microsoft Azure:

  1. Selecione Novo Usuário>Criar Novo Usuário.
  2. Use o modelo Criar usuário.
  3. Forneça as informações do usuário na área Identity.
  4. Você pode gerar uma senha inicial ou atribuir uma senha inicial que o usuário alterará quando entrar pela primeira vez. Se você usar a senha gerada pelo portal, anote-a agora.
  5. Selecione Criar para criar o usuário. Quando a interface Criar usuário for fechada, selecione Atualizar para atualizar a lista de usuários e mostrar o novo usuário.
  6. Para os exemplos deste artigo, atribua um número de telefone celular ao novo usuário selecionando o nome dele na lista de usuários, selecionando Propriedades e editando as informações de contato para fornecer um número de telefone celular.

Para atribuir usuários ao aplicativo sem funções de aplicativo:

  1. Na área ME-ID do portal do Microsoft Azure, abra Aplicativos empresariais.
  2. Selecione o aplicativo na lista.
  3. Selecione Usuários e grupos.
  4. Selecione Adicionar usuário/grupo.
  5. Selecione um usuário.
  6. Selecione o botão Atribuir.

Para atribuir usuários ao aplicativo com funções de aplicativo:

  1. Adicione funções ao registro do aplicativo no portal do Microsoft Azure seguindo as diretrizes em ASP.NET Core Blazor WebAssembly com grupos e funções do Microsoft Entra ID.
  2. Na área ME-ID do portal do Microsoft Azure, abra Aplicativos empresariais.
  3. Selecione o aplicativo na lista.
  4. Selecione Usuários e grupos.
  5. Selecione Adicionar usuário/grupo.
  6. Selecione um usuário e escolha sua função para acessar o aplicativo. Várias funções são atribuídas a um usuário repetindo o processo de adição do usuário ao aplicativo até que todas as funções de um usuário sejam atribuídas. Os usuários com várias funções são listados uma vez para cada função atribuída na lista de usuários Usuários e grupos do aplicativo.
  7. Selecione o botão Atribuir.

DefaultAccessTokenScopes versus AdditionalScopesToConsent

Os exemplos deste artigo fornecem escopos da API do Graph com DefaultAccessTokenScopes, não AdditionalScopesToConsent.

AdditionalScopesToConsent não é usado porque não é possível provisionar escopos da API do Graph para os usuários quando eles entram no aplicativo pela primeira vez com a MSAL por meio da IU de consentimento do Azure. Quando o usuário tenta acessar a API do Graph pela primeira vez com o SDK do Graph, ele é confrontado com uma exceção:

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

Depois que um usuário provisiona os escopos da API do Graph fornecidos por DefaultAccessTokenScopes, o aplicativo pode usar AdditionalScopesToConsent para um entrada de usuário subsequente. No entanto, alterar o código do aplicativo não faz sentido para um aplicativo de produção que requer a adição periódica de novos usuários com escopos do Graph delegados ou a adição de novos escopos da API do Graph delegados ao aplicativo.

A discussão anterior sobre como provisionar escopos para o acesso à API do Graph quando o usuário entra no aplicativo pela primeira vez se aplica somente a:

  • Aplicativos que adotam o SDK do Graph.
  • Aplicativos que usam um HttpClient nomeado para acesso à API do Graph que solicita aos usuários o consentimento para os escopos do Graph em sua primeira entrada no aplicativo.

Ao usar um HttpClient nomeado que não solicita o consentimento dos usuários para os escopos do Graph na primeiro entrada, os usuários são redirecionados para a interface do usuário de consentimento do Azure para o consentimento dos escopos da API do Graph quando solicitam acesso à API do Graph pela primeira vez por meio do DelegatingHandler do HttpClient pré-configurado. Quando os escopos do Graph não são consentidos inicialmente com a abordagem HttpClient nomeada, nem DefaultAccessTokenScopes nem AdditionalScopesToConsent são chamados pelo aplicativo. Para obter mais informações, consulte a cobertura nomeada HttpClient neste artigo.

Soluções Blazor WebAssembly hospedadas

Os exemplos neste artigo referem-se ao uso do SDK do Graph ou um HttpClient nomeado com API do Graph diretamente de um aplicativo Blazor WebAssembly autônomo ou diretamente do aplicativo Client de uma Blazor WebAssemblysolução hospedada. Um cenário adicional que não é abordado por este artigo é que um aplicativo Client de uma solução hospedada chame o aplicativo Server da solução por meio da API Web e, em seguida, o aplicativo Server use o SDK/API do Graph para chamar o Microsoft Graph e retornar dados para o aplicativo Client. Embora essa seja uma abordagem com suporte, ela não é abordada por este artigo. Se você quiser adotar essa abordagem:

  • Siga as diretrizes em Chamar uma API Web de um aplicativo ASP.NET Core Blazor para os aspectos da API Web sobre como emitir solicitações para o aplicativo Server do aplicativo Client e retornar dados para o aplicativo Client.
  • Siga as diretrizes na documentação primária do Microsoft Graph para usar o SDK do Graph com um aplicativo de ASP.NET Core típico, que nesse cenário é o aplicativo Server da solução. Se você usar o modelo de projeto do Blazor WebAssembly para criar a solução do Blazor WebAssembly hospedada (ASP.NET Core Hospedada/-h|--hosted) com autorização organizacional (organização única/SingleOrg ou várias organizações/MultiOrg) e a opção Microsoft Graph (plataforma de identity da Microsoft>Serviços Conectados>Adicionar permissões do Microsoft Graph no Visual Studio ou a opção --calls-graph com o comando dotnet new da CLI do .NET), o aplicativo Server da solução será configurado para usar o SDK do Graph quando a solução for criada a partir do modelo de projeto.

Recursos adicionais

Orientação geral

  • Documentação do Microsoft Graph
  • Aplicativo Blazor WebAssembly de exemplo do Microsoft Graph: este exemplo demonstra como usar o SDK do .NET do Microsoft Graph para acessar dados no Office 365 de aplicativos Blazor WebAssembly.
  • Crie aplicativos .NET com o tutorial do Microsoft Graph e o exemplo de um aplicativo ASP.NET Core do Microsoft Graph: esses recursos são mais apropriados para soluções hospedadasBlazor WebAssembly, em que o aplicativo Server esteja configurado para acessar o Microsoft Graph como um aplicativo ASP.NET Core típico em nome do aplicativo Client. O aplicativo Client usa a API Web para fazer solicitações ao aplicativo Server para dados do Graph. Embora esses recursos não se apliquem diretamente à chamada do Graph dos aplicativos Blazor WebAssemblydo lado do cliente, a configuração do aplicativo ME-ID e as práticas de codificação do Microsoft Graph nos recursos vinculados são relevantes para aplicativos autônomos Blazor WebAssembly e devem ser consultadas para melhores práticas gerais.

Diretrizes de segurança