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 10 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. 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.

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

São abordadas duas abordagens:

  • 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 gráfico na parte superior deste artigo para adotar essa abordagem.

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

As orientações neste artigo não se destinam a substituir a documentação do Microsoft Graph e as orientações de segurança do Azure noutros 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 as práticas recomendadas da Microsoft para limitar as vulnerabilidades de seus aplicativos.

Abordagens adicionais para trabalhar com o Microsoft Graph e 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 em seu comentário de abertura porque o repositório de exemplo do Azure (Azure-Samples) contém muitos exemplos. Descreva o problema em detalhes e inclua 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á-los no repositório público.

Para fornecer feedback ou procurar ajuda com este artigo ou ASP.NET Core, consulte ASP.NET Fundamentos principaisBlazor.

Importante

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

Está disponível suporte para o uso de uma aplicação hospedada Blazor WebAssembly, na qual a aplicação Server utiliza o SDK/API do Graph para fornecer dados do Graph à aplicação Client através da API da web. Para obter mais informações, consulte a seção Soluções hospedadas Blazor WebAssembly deste artigo.

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

As orientações a seguir se aplicam ao Microsoft Graph v5.

O SDK do Microsoft Graph para uso em Blazor aplicativos é chamado de Microsoft Graph .NET Client Library.

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

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

Observação

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

No portal do Azure, conceda permissões delegadas (escopos) † para dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo neste artigo, o registro do aplicativo deve incluir permissão delegada para ler dados do usuário (Microsoft.Graph>User.Read escopo em permissões de API, Tipo: Delegado). O User.Read escopo permite que os usuários entrem no aplicativo e permite que o aplicativo leia o perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral de permissões e consentimento na plataforma de identidade 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 Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao referir-se a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto User.Read é o mesmo que user.read. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente entre os códigos do aplicativo.

Depois de adicionar os âmbitos da API do Microsoft Graph ao registo da aplicação no portal do Azure, adicione a seguinte configuração das definições de aplicação ao ficheiro dentro da aplicação, o qual inclui a URL base do Graph com a versão e os âmbitos do Microsoft Graph. No exemplo a seguir, especifica-se o escopo User.Read para os exemplos nas secções seguintes 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 está um exemplo de um arquivo de configuração completo wwwroot/appsettings.json para um aplicativo que usa ME-ID como seu provedor de identidade, onde a leitura de dados do usuário (user.read escopo) é 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 do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

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

Adicione a seguinte GraphClientExtensions classe à aplicação autónoma ou à aplicação Client de uma Blazor WebAssemblysolução hospedada. Os escopos são fornecidos para a Scopes propriedade do AccessTokenRequestOptions no AuthenticateRequestAsync método.

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 DefaultAccessTokenScopes seção 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 assume como padrão o endereço base e User.Read os escopos 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 a API do Graph a partir de um componente usando o SDK do Graph

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

Para qualquer usuário de teste que você criar no ME-ID, certifique-se de fornecer ao perfil de 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 para a 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>

Sugestão

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 Graph SDK localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/incognito para cada teste para evitar que cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo do ASP.NET Core Blazor WebAssembly com o 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 localização do escritório para um usuário a partir dos dados do perfil de usuário ME-ID. A aplicação deve ter o âmbito da User.Read API do Graph 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 do escritório em seu perfil ME-ID, que pode ser adicionado por meio do portal do Azure.

No seguinte fábrica de conta de utilizador personalizada:

  • Um ILogger (logger) é incluído para conveniência no caso de você desejar registrar informações ou erros no CreateUserAsync método.
  • No caso de um AccessTokenNotAvailableException ser lançado, o usuário é redirecionado para o provedor de identidade para entrar em sua conta. Ações adicionais ou diferentes podem ser tomadas quando a solicitação de um token de acesso falha. Por exemplo, a aplicação pode registar o AccessTokenNotAvailableException e criar um pedido de suporte para investigação adicional.
  • RemoteUserAccount representa a conta do utilizador. Se a aplicação exigir uma classe de conta de usuário personalizada que se estenda RemoteUserAccount, troque sua classe de conta de usuário personalizada pela 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 a fábrica de contas de utilizador personalizadas.

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

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

O exemplo nesta secção baseia-se na abordagem de leitura da URL base, com a versão e os âmbitos, a partir da configuração da aplicação, por meio da secção MicrosoftGraph no ficheiro wwwroot/appsettings.json. As seguintes linhas já devem estar presentes no Program arquivo seguindo as orientações anteriores neste artigo:

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, localize a chamada para o método de extensão AddMsalAuthentication. Atualize o código para o seguinte, que inclui uma chamada para AddAccountClaimsPrincipalFactory que adiciona uma fábrica principal de declarações de conta com o CustomAccountFactory.

Se a aplicação usar uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, substitua a classe de conta de usuário personalizada por 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 UserClaims componente para estudar as declarações do usuário depois que o usuário se autentica com 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 para a 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 Graph SDK localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/incognito para cada teste para evitar que cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

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

O SDK do Microsoft Graph para uso em Blazor aplicativos é chamado de Microsoft Graph .NET Client Library.

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

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

Observação

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

No portal do Azure, conceda permissões delegadas (escopos) † para dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo neste artigo, o registro do aplicativo deve incluir permissão delegada para ler dados do usuário (Microsoft.Graph>User.Read escopo em permissões de API, Tipo: Delegado). O User.Read escopo permite que os usuários entrem no aplicativo e permite que o aplicativo leia o perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral de permissões e consentimento na plataforma de identidade 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 Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao referir-se a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto User.Read é o mesmo que user.read. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente entre os códigos do aplicativo.

Depois de adicionar os âmbitos da API do Microsoft Graph ao registo da aplicação no portal do Azure, adicione a seguinte configuração das definições de aplicação ao ficheiro dentro da aplicação, o qual inclui a URL base do Graph com a versão e os âmbitos do Microsoft Graph. No exemplo a seguir, especifica-se o escopo User.Read para os exemplos nas secções seguintes 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 está um exemplo de um arquivo de configuração completo wwwroot/appsettings.json para um aplicativo que usa ME-ID como seu provedor de identidade, onde a leitura de dados do usuário (user.read escopo) é 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 do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

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

Adicione a seguinte GraphClientExtensions classe à aplicação autónoma ou à aplicação Client de uma Blazor WebAssemblysolução hospedada. Os escopos são fornecidos para a Scopes propriedade do AccessTokenRequestOptions no AuthenticateRequestAsync método. O IHttpProvider.OverallTimeout é estendido do valor padrão de 100 segundos para 300 segundos para dar mais HttpClient 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 DefaultAccessTokenScopes seção 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 ficheiro Program, adicione os serviços e a configuração do cliente Graph 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 a API do Graph a partir de um componente usando o SDK do Graph

O componente a seguir UserData usa uma injeção GraphServiceClient para obter os dados de perfil ME-ID do usuário e exibir seu número de telefone celular. Para qualquer usuário de teste que você criar no ME-ID, certifique-se de fornecer ao perfil de 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 para a 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>

Sugestão

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 Graph SDK localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/incognito para cada teste para evitar que cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo do ASP.NET Core Blazor WebAssembly com o 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 localização do escritório para um usuário a partir dos dados do perfil de usuário ME-ID. A aplicação deve ter o âmbito da User.Read API do Graph 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 do escritório em seu perfil ME-ID, que pode ser adicionado por meio do portal do Azure.

No seguinte fábrica de conta de utilizador personalizada:

  • Um ILogger (logger) é incluído para conveniência no caso de você desejar registrar informações ou erros no CreateUserAsync método.
  • No caso de um AccessTokenNotAvailableException ser lançado, o usuário é redirecionado para o provedor de identidade para entrar em sua conta. Ações adicionais ou diferentes podem ser tomadas quando a solicitação de um token de acesso falha. Por exemplo, a aplicação pode registar o AccessTokenNotAvailableException e criar um pedido de suporte para investigação adicional.
  • RemoteUserAccount representa a conta do utilizador. Se a aplicação exigir uma classe de conta de usuário personalizada que se estenda RemoteUserAccount, troque sua classe de conta de usuário personalizada pela 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 a fábrica de contas de utilizador personalizadas.

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

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

O exemplo nesta secção baseia-se na abordagem de leitura da URL base, com a versão e os âmbitos, a partir da configuração da aplicação, por meio da secção MicrosoftGraph no ficheiro wwwroot/appsettings.json. As seguintes linhas já devem estar presentes no Program arquivo seguindo as orientações anteriores neste artigo:

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, localize a chamada para o método de extensão AddMsalAuthentication. Atualize o código para o seguinte, que inclui uma chamada para AddAccountClaimsPrincipalFactory que adiciona uma fábrica principal de declarações de conta com o CustomAccountFactory.

Se a aplicação usar uma classe de conta de usuário personalizada que estenda o RemoteUserAccount, substitua a classe de conta de usuário personalizada por 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 UserClaims componente para estudar as declarações do usuário depois que o usuário se autentica com 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 para a 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 Graph SDK localmente, recomendamos o uso de uma nova sessão de navegador InPrivate/incognito para cada teste para evitar que cookies persistentes interfiram nos testes. Para obter mais informações, consulte Proteger um aplicativo autônomo do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

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

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

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

Observação

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

No portal do Azure, conceda permissões delegadas (escopos) † para dados do Microsoft Graph que o aplicativo deve ser capaz de acessar em nome de um usuário. Para o exemplo neste artigo, o registro do aplicativo deve incluir permissão delegada para ler dados do usuário (Microsoft.Graph>User.Read escopo em permissões de API, Tipo: Delegado). O User.Read escopo permite que os usuários entrem no aplicativo e permite que o aplicativo leia o perfil e as informações da empresa dos usuários conectados. Para obter mais informações, consulte Visão geral de permissões e consentimento na plataforma de identidade 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 Azure. A menos que o texto esteja se referindo ao portal do Azure, este artigo usa escopo/escopos ao referir-se a permissões do Graph.

Os escopos não diferenciam maiúsculas de minúsculas, portanto User.Read é o mesmo que user.read. Sinta-se à vontade para usar qualquer um dos formatos, mas recomendamos uma escolha consistente entre os códigos do aplicativo.

Depois de adicionar os âmbitos da API do Microsoft Graph ao registo da aplicação no portal do Azure, adicione a seguinte configuração das definições de aplicação ao ficheiro dentro da aplicação, o qual inclui a URL base do Graph com a versão e os âmbitos do Microsoft Graph. No exemplo a seguir, especifica-se o escopo User.Read para os exemplos nas secções seguintes 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 está um exemplo de um arquivo de configuração completo wwwroot/appsettings.json para um aplicativo que usa ME-ID como seu provedor de identidade, onde a leitura de dados do usuário (user.read escopo) é 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 do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Crie a seguinte classe e configuração de projeto no ficheiro GraphAuthorizationMessageHandler para trabalhar com a Graph API Program. 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 final (/) do URL autorizado é necessária. O código anterior cria a seguinte URL autorizada a partir da configuração das definições do aplicativo ou utiliza por defeito a seguinte URL autorizada caso a configuração esteja ausente: https://graph.microsoft.com/v1.0/.

No arquivo Program, configure o nome HttpClient para a Graph API.

builder.Services.AddTransient<GraphAuthorizationMessageHandler>();

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

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

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

Chamar a Graph API a partir de um componente nomeado HttpClient

A UserInfo.cs classe designa as propriedades de perfil de usuário necessárias com o JsonPropertyNameAttribute atributo 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 a seguir UserData , um HttpClient é criado para que a API do Graph emita uma solicitação para os dados de perfil do usuário. O me recurso (me) é adicionado à URL base com a versão para a solicitação da API do Graph. Os dados JSON retornados pelo Graph são desserializados nas propriedades da UserInfo classe. No exemplo a seguir, o número de telefone celular é obtido. Você pode adicionar um código semelhante para incluir a localização do escritório no perfil do usuário ME-ID, se desejar (userInfo.OfficeLocation). Se a solicitação de token de acesso falhar, o usuário será redirecionado para entrar no aplicativo para obter 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 para a 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>

Sugestão

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ário para escopos da API do Graph:

  1. O novo utilizador inicia sessão na aplicação pela primeira vez.
  2. O usuário consente em usar o aplicativo na interface do 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 do usuário de consentimento do Azure para consentir com os escopos da API do Graph.
  5. São retornados os dados do utilizador da Graph API.

Se preferir que o provisionamento de escopo (consentimento para escopos da API do Graph) ocorra no login inicial, forneça os escopos para autenticação MSAL como escopos Program de token de acesso padrão no arquivo:

+ 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 DefaultAccessTokenScopes seção 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 utilizador inicia sessão na aplicação pela primeira vez.
  2. O utilizador consente na utilização dos âmbitos da aplicação e da API do Graph na interface 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. São retornados os dados do utilizador da Graph API.

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

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

No exemplo a seguir, o aplicativo cria declarações de número de telefone celular e localização do escritório para o usuário a partir dos dados do perfil de usuário ME-ID. A aplicação deve ter o âmbito da User.Read API do Graph 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 podem ser adicionados por meio do portal do Azure aos seus perfis de usuário.

Se você ainda não adicionou a UserInfo classe ao aplicativo seguindo as orientações anteriores neste artigo, adicione a classe a seguir e designe as propriedades de perfil de usuário necessárias com o JsonPropertyNameAttribute atributo 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 seguinte fábrica de conta de utilizador personalizada:

  • Um ILogger (logger) é incluído para conveniência no caso de você desejar registrar informações ou erros no CreateUserAsync método.
  • No caso de um AccessTokenNotAvailableException ser lançado, o usuário é redirecionado para o provedor de identidade para entrar em sua conta. Ações adicionais ou diferentes podem ser tomadas quando a solicitação de um token de acesso falha. Por exemplo, a aplicação pode registar o AccessTokenNotAvailableException e criar um pedido de suporte para investigação adicional.
  • RemoteUserAccount representa a conta do utilizador. Se a aplicação exigir uma classe de conta de utilizador personalizada que se estenda a RemoteUserAccount, substitua a classe de conta de utilizador personalizada por RemoteUserAccount no 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 conta de utilizador personalizada. Comece confirmando que o Program arquivo usa o Microsoft.AspNetCore.Components.WebAssembly.Authentication namespace:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

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

Se o aplicativo usar uma classe de conta de usuário personalizada que se estende de RemoteUserAccount, substitua a classe de conta de usuário personalizada do seu aplicativo pela RemoteUserAccount no código 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 autenticação ME-ID com MSAL. Existem padrões semelhantes para autenticação OIDC e API. Para obter mais informações, consulte os exemplos na seção Personalizar o usuário com uma declaração de carga útil do artigo Cenários de segurança adicionais do ASP.NET CoreBlazor WebAssembly.

Você pode usar o seguinte UserClaims componente para estudar as declarações do usuário depois que o usuário se autentica com 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 para a 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 Graph API localmente, recomendamos o uso de uma nova sessão do navegador InPrivate/incognito para cada teste para evitar que cookies persistentes interfiram no teste. Para obter mais informações, consulte Proteger um aplicativo autônomo do ASP.NET Core Blazor WebAssembly com o Microsoft Entra ID.

Atribuir utilizadores a um registo de aplicação com ou sem funções de aplicação

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

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

  1. Selecione Novo usuário>Criar novo usuário.
  2. Use o modelo Criar usuário .
  3. Fornecer as informações do usuário na Identity área.
  4. Você pode gerar uma senha inicial ou atribuir uma senha inicial que o usuário altera quando entra pela primeira vez. Se usares a senha gerada pelo portal, toma nota agora.
  5. Selecione Criar para criar o usuário. Quando Criar nova interface do usuário for fechado, selecione Atualizar para atualizar a lista de usuários e mostrar o novo usuário.
  6. Para os exemplos neste artigo, atribua um número de telefone celular ao novo usuário selecionando seu nome 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 Azure, abra Aplicativos corporativos.
  2. Selecione o aplicativo na lista.
  3. Selecione Usuários e grupos.
  4. Selecione Adicionar usuário/grupo.
  5. Selecione um utilizador.
  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 Azure seguindo as orientações no ASP.NET Core Blazor WebAssembly com grupos e funções do Microsoft Entra ID.
  2. Na área ME-ID do portal do Azure, abra Aplicativos corporativos.
  3. Selecione o aplicativo na lista.
  4. Selecione Usuários e grupos.
  5. Selecione Adicionar usuário/grupo.
  6. Selecione um usuário e selecione sua função para acessar o aplicativo. Várias funções são atribuídas a um usuário repetindo o processo de adicionar o usuário ao aplicativo até que todas as funções para 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 Usuários e grupos de usuários do aplicativo.
  7. Selecione o botão Atribuir.

DefaultAccessTokenScopes Versus AdditionalScopesToConsent

Os exemplos neste artigo provisionam 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 utilizadores quando iniciam sessão na aplicação pela primeira vez com o MSAL através da interface 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 de um utilizador provisionar os espaços da API Graph fornecidos por meio de DefaultAccessTokenScopes, a aplicação pode utilizar o AdditionalScopesToConsent para uma sessão subsequente de início de utilizador. No entanto, alterar o código do aplicativo não faz sentido para um aplicativo de produção que exige a adição periódica de novos usuários com escopos delegados do Graph ou a adição de novos escopos delegados da API do Graph ao aplicativo.

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

  • Aplicativos que adotam o SDK do Graph.
  • Aplicativos que usam um nome HttpClient para acesso à API do Graph que solicita aos usuários que consintam com os escopos do Graph em seu primeiro login no aplicativo.

Ao usar um nome HttpClient que não pede aos usuários que consintam com os escopos do Graph em seu primeiro login, os usuários são redirecionados para a interface do usuário de consentimento do Azure para consentimento de escopos da API do Graph quando solicitam acesso à API do Graph pela primeira vez por meio DelegatingHandler do pré-configurado, chamado HttpClient. Quando os escopos do Graph não são consentidos inicialmente com a abordagem denominada HttpClient, nem DefaultAccessTokenScopes nem AdditionalScopesToConsent são chamados pela aplicação. Para obter mais informações, consulte a cobertura nomeada HttpClient neste artigo.

Soluções hospedadas Blazor WebAssembly

Os exemplos neste artigo dizem respeito ao uso do SDK do Graph ou de um nome HttpClient com a API do Graph diretamente de um aplicativo autônomo Blazor WebAssembly ou diretamente do Client aplicativo de uma Blazor WebAssembly hospedada. Um cenário adicional que não é abordado neste artigo é que um Client aplicativo de uma solução hospedada chame o Server aplicativo da solução via API da Web e, em seguida, o Server aplicativo usa o SDK do Graph/API para chamar o Microsoft Graph e retornar dados para o Client aplicativo. Embora esta seja uma abordagem suportada, não é abordada por este artigo. Se desejar adotar esta abordagem:

  • Siga as orientações em Chamar uma API da Web de um aplicativo ASP.NET Core Blazor para os aspetos da API da Web sobre como Server emitir solicitações para o Client aplicativo a partir do aplicativo e retornar dados para o Client aplicativo.
  • Siga as orientações na documentação principal do Microsoft Graph para usar o SDK do Graph com um aplicativo ASP.NET Core típico, que neste cenário é o Server aplicativo da solução. Se utilizar o modelo de projeto Blazor WebAssembly para criar a solução hospedada Blazor WebAssembly (ASP.NET Core Hosted/-h|--hosted) com autorização organizacional (organização única/SingleOrg ou organização múltipla/MultiOrg) e a opção Microsoft Graph (Microsoft identity platform>Connected Services>Adicionar permissões do Microsoft Graph no Visual Studio ou a opção --calls-graph com o comando .NET CLI dotnet new), a aplicação Server da solução é configurada para utilizar o SDK do Graph quando a solução é criada a partir do modelo de projeto.

Recursos adicionais

Orientações gerais

  • documentação do Microsoft Graph
  • Aplicativo de exemplo Blazor WebAssembly do Microsoft Graph: este exemplo demonstra como usar o SDK do Microsoft Graph .NET para acessar dados no Office 365 a partir de Blazor WebAssembly aplicativos.
  • Crie aplicações .NET com o tutorial do Microsoft Graph e a aplicação de exemplo ASP.NET Core do Microsoft Graph: estes recursos são mais apropriados para soluções hospedadasBlazor WebAssembly, onde a aplicação é configurada para aceder ao Server Microsoft Graph como uma aplicação ASP.NET Core típica em nome da Client aplicação. A aplicação Client utiliza a API web para enviar pedidos à aplicação Server para obter dados do Graph. Embora esses recursos não se apliquem diretamente à chamada do Graph a partir de aplicativos do ladoBlazor WebAssembly 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 obter práticas recomendadas gerais.

Orientações de segurança