Partilhar via


ASP.NET Core do lado do servidor e cenários de segurança adicionais Blazor Web App

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 .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.

Este artigo explica como configurar Blazor do lado do servidor para cenários de segurança adicionais, incluindo como passar tokens para um aplicativo Blazor.

Observação

Os exemplos de código neste artigo adotam tipos de referência anuláveis (NRTs) ede análise estática de estado nulo do compilador .NET, que são suportados no ASP.NET Core no .NET 6 ou posterior. Ao direcionar para o .NET 5 ou versões anteriores, remova a designação de tipo nulo (?) dos tipos string?, TodoItem[]?, WeatherForecast[]? e IEnumerable<GitHubBranch>? nos exemplos do artigo.

Passar tokens para um aplicativo Blazor do lado do servidor

Esta secção aplica-se aos Blazor Web Apps. Para Blazor Server, consulte a seção .NET 7 versão deste artigo.

Se você quiser apenas usar tokens de acesso para fazer chamadas de API da Web de um Blazor Web App com um cliente HTTP nomeado, consulte a seção Usar um manipulador de token para chamadas de API da Web , que explica como usar uma DelegatingHandler implementação para anexar o token de acesso de um usuário a solicitações de saída. As diretrizes a seguir nesta seção são para desenvolvedores que precisam de tokens de acesso, tokens de atualização e outras propriedades de autenticação do lado do servidor para outros fins.

Para salvar tokens e outras propriedades de autenticação para uso no lado do servidor em Blazor Web Apps, recomendamos o uso de IHttpContextAccessor/HttpContext (IHttpContextAccessor, HttpContext). A leitura de tokens do , inclusive como um HttpContext, é suportada para a obtenção de tokens para uso durante a renderização interativa do servidor se os tokens forem obtidos durante a renderização estática do lado do IHttpContextAccessorservidor (SSR estático) ou pré-renderização. No entanto, os tokens não são atualizados se o utilizador se autenticar depois de o circuito ser estabelecido, uma vez que o HttpContext é capturado no início da SignalR conexão. Além disso, o uso de AsyncLocal<T> por IHttpContextAccessor significa que deve ter cuidado para não perder o contexto de execução antes de ler o HttpContext. Para obter mais informações, consulte IHttpContextAccessor/HttpContext em aplicações ASP.NET Core Blazor.

Em uma classe de serviço, obtenha acesso aos membros do namespace Microsoft.AspNetCore.Authentication para exibir o GetTokenAsync método em HttpContext. Uma abordagem alternativa, que está comentada no exemplo a seguir, é chamar AuthenticateAsync em HttpContext. Para o retorno AuthenticateResult.Properties, ligue para GetTokenValue.

using Microsoft.AspNetCore.Authentication;

public class AuthenticationProcessor(IHttpContextAccessor httpContextAccessor)
{
    public async Task<string?> GetAccessToken()
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        // Approach 1: Call 'GetTokenAsync'
        var accessToken = await httpContextAccessor.HttpContext
            .GetTokenAsync("access_token");

        // Approach 2: Authenticate the user and call 'GetTokenValue'
        /*
        var authResult = await httpContextAccessor.HttpContext.AuthenticateAsync();
        var accessToken = authResult?.Properties?.GetTokenValue("access_token");
        */

        return accessToken;
    }
}

O serviço está registado no ficheiro de projeto do Program servidor.

builder.Services.AddScoped<AuthenticationProcessor>();

AuthenticationProcessor pode ser injetado em serviços do lado do servidor, por exemplo, em um DelegatingHandler para um pré-configurado HttpClient. O exemplo a seguir é apenas para fins de demonstração ou no caso de você precisar executar processamento especial no AuthenticationProcessor serviço, pois você pode simplesmente injetar IHttpContextAccessor e obter o token diretamente para chamar APIs da Web externas (para obter mais informações sobre como usar IHttpContextAccessor diretamente para chamar APIs da Web, consulte a seção Usar um manipulador de token para chamadas de API da Web ).

using System.Net.Http.Headers;

public class TokenHandler(AuthenticationProcessor authProcessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var accessToken = authProcessor.GetAccessToken();

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

O manipulador de token é registrado e atua como o manipulador de delegação para um cliente HTTP nomeado no Program arquivo:

builder.Services.AddHttpContextAccessor();

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ?? 
          throw new Exception("Missing base address!")))
      .AddHttpMessageHandler<TokenHandler>();

Atenção

Certifique-se de que os tokens nunca sejam transmitidos e manipulados pelo cliente (.Client projeto), por exemplo, em um componente que adote a renderização interativa automática e seja renderizado no cliente ou por um serviço do cliente. Sempre faça com que o cliente chame o servidor (projeto) para processar solicitações com tokens. Tokens e outros dados de autenticação nunca devem sair do servidor.

Para componentes automáticos interativos, consulte Autenticação e autorização ASP.NET CoreBlazor, que demonstra como deixar tokens de acesso e outras propriedades de autenticação no servidor. Além disso, considere adotar o padrão Backend-for-Frontend (BFF), que adota uma estrutura de chamada semelhante e é descrito em Proteger um ASP.NET Core Blazor Web App com OpenID Connect (OIDC) para provedores OIDC e Proteger um ASP.NET Core Blazor Web App com Microsoft Entra ID para Microsoft Identity Web com Entra.

Usar um manipulador de token para chamadas de API da Web

A abordagem a seguir destina-se a anexar o token de acesso de um usuário a solicitações de saída, especificamente para fazer chamadas de API da Web para aplicativos de API da Web externos. A abordagem é mostrada para um Blazor Web App que adota a renderização global do Servidor Interativo, mas a mesma abordagem geral se aplica aos Blazor Web Apps que adotam o modo de renderização automática interativa global. O conceito importante a ter em mente é que o acesso ao HttpContext usando IHttpContextAccessor é realizado apenas no servidor.

Para obter uma demonstração das orientações nesta seção, consulte os aplicativos de exemplo BlazorWebAppOidc e BlazorWebAppOidcServer (.NET 8 ou posterior) nas amostras do repositório GitHub Blazor. Os exemplos adotam um modo de renderização interativo global e autenticação OIDC com o Microsoft Entra sem usar pacotes específicos do Entra. Os exemplos demonstram como passar um token de acesso JWT para chamar uma API da Web segura.

A plataforma de identidade da Microsoft com pacotes da Web da Microsoft Identity para o Microsoft Entra ID fornece uma API para chamar APIs da Web a partir de Blazor Web Apps, com gerenciamento e renovação automática de tokens. Para obter mais informações, consulte Proteger um ASP.NET Core Blazor Web App com Microsoft Entra ID e os aplicativos de exemplo BlazorWebAppEntraBlazorWebAppEntraBff (.NET 9 ou posterior) no Blazor repositório GitHub de exemplos.

Subclasse DelegatingHandler para anexar o token de acesso de um usuário a solicitações de saída. O manipulador de token só é executado no servidor, portanto, usar HttpContext é seguro.

TokenHandler.cs:

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Authentication;

public class TokenHandler(IHttpContextAccessor httpContextAccessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        var accessToken = await httpContextAccessor.HttpContext.GetTokenAsync("access_token");

        if (accessToken is null)
        {
            throw new Exception("No access token");
        }

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

Observação

Para obter orientação sobre como aceder a um AuthenticationStateProvider a partir de um DelegatingHandler, consulte a seção Acessar AuthenticationStateProvider no middleware de solicitação de saída.

No arquivo do Program projeto, o manipulador de token (TokenHandler) é registrado como um serviço com escopo e especificado como um manipulador de mensagens do cliente HTTP nomeado com AddHttpMessageHandler.

No exemplo a seguir, o espaço reservado {HTTP CLIENT NAME} é o nome do HttpClient, e o espaço reservado {BASE ADDRESS} é o URI do endereço base da API da Web. Para obter mais informações sobre AddHttpContextAccessor, consulte Blazor.

Em Program.cs:

builder.Services.AddHttpContextAccessor();

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("{HTTP CLIENT NAME}",
      client => client.BaseAddress = new Uri("{BASE ADDRESS}"))
      .AddHttpMessageHandler<TokenHandler>();

Exemplo:

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri("https://localhost:7277"))
      .AddHttpMessageHandler<TokenHandler>();

Você pode fornecer o endereço base do cliente HTTP da configuração com builder.Configuration["{CONFIGURATION KEY}"], onde o marcador {CONFIGURATION KEY} é a chave de configuração:

new Uri(builder.Configuration["ExternalApiUri"] ?? throw new IOException("No URI!"))

Em appsettings.json, especifique o arquivo ExternalApiUri. O seguinte exemplo define o valor para o endereço localhost da API web externa como https://localhost:7277:

"ExternalApiUri": "https://localhost:7277"

Neste ponto, um HttpClient criado por um componente pode fazer solicitações de API da Web seguras. No exemplo a seguir, o {REQUEST URI} é o URI da solicitação relativa e o espaço reservado {HTTP CLIENT NAME} é o nome do HttpClient:

using var request = new HttpRequestMessage(HttpMethod.Get, "{REQUEST URI}");
var client = ClientFactory.CreateClient("{HTTP CLIENT NAME}");
using var response = await client.SendAsync(request);

Exemplo:

using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);

Recursos adicionais estão planeados para Blazor, que são monitorizados através do Access AuthenticationStateProvider no middleware da solicitação de saída (dotnet/aspnetcore #52379). Problema ao fornecer Token de Acesso para HttpClient no modo Servidor Interativo (dotnet/aspnetcore #52390) é um problema fechado que contém discussão útil e possíveis estratégias de solução alternativa para casos de uso avançados.

Os tokens disponíveis fora dos componentes Razor em um aplicativo de Blazor do lado do servidor podem ser passados para componentes com a abordagem descrita nesta seção. O exemplo nesta secção foca-se em passar tokens de acesso, atualização e proteção contra falsificação de solicitação (XSRF) para a aplicação, mas a abordagem é válida para outros estados de contexto HTTP.

Observação

Passar para os componentes Razor o token XSRF é útil em cenários onde os componentes realizam POST para Identity ou outros endpoints que exigem validação. Se seu aplicativo exigir apenas tokens de acesso e atualização, você poderá remover o código de token XSRF do exemplo a seguir.

Autentique o aplicativo como faria com um aplicativo Razor Pages ou MVC normal. Forneça e guarde os tokens na autenticação cookie.

No ficheiro Program:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

Em Startup.cs:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

Em Startup.cs:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

...

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
});

Opcionalmente, escopos adicionais são acrescentados com options.Scope.Add("{SCOPE}");, sendo o marcador {SCOPE} o escopo adicional a ser adicionado.

Defina um serviço de provedor de token com escopo que possa ser usado dentro da Blazor aplicação para resolver os tokens da injeção de dependência (DI).

TokenProvider.cs:

public class TokenProvider
{
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; }
    public string? XsrfToken { get; set; }
}

No arquivo Program, adicione serviços para:

  • IHttpClientFactory: Usado em uma classe WeatherForecastService que obtém dados meteorológicos de uma API de servidor com um token de acesso.
  • TokenProvider: Mantém os tokens de acesso e atualização.
builder.Services.AddHttpClient();
builder.Services.AddScoped<TokenProvider>();

Em Startup.ConfigureServices de Startup.cs, adicione serviços para:

  • IHttpClientFactory: Usado em uma classe WeatherForecastService que obtém dados meteorológicos de uma API de servidor com um token de acesso.
  • TokenProvider: Mantém os tokens de acesso e atualização.
services.AddHttpClient();
services.AddScoped<TokenProvider>();

Defina uma classe para passar no estado inicial do aplicativo com os tokens de acesso e atualização.

InitialApplicationState.cs:

public class InitialApplicationState
{
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; }
    public string? XsrfToken { get; set; }
}

No arquivo Pages/_Host.cshtml, crie uma instância de InitialApplicationState e passe-a como um parâmetro para o aplicativo:

No arquivo Pages/_Layout.cshtml, crie uma instância de InitialApplicationState e passe-a como um parâmetro para o aplicativo:

No arquivo Pages/_Host.cshtml, crie uma instância de InitialApplicationState e passe-a como um parâmetro para o aplicativo:

@using Microsoft.AspNetCore.Authentication
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

...

@{
    var tokens = new InitialApplicationState
    {
        AccessToken = await HttpContext.GetTokenAsync("access_token"),
        RefreshToken = await HttpContext.GetTokenAsync("refresh_token"),
        XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken
    };
}

<component ... param-InitialState="tokens" ... />

No componente App (App.razor), resolva o serviço e inicialize-o com os dados do parâmetro:

@inject TokenProvider TokenProvider

...

@code {
    [Parameter]
    public InitialApplicationState? InitialState { get; set; }

    protected override Task OnInitializedAsync()
    {
        TokenProvider.AccessToken = InitialState?.AccessToken;
        TokenProvider.RefreshToken = InitialState?.RefreshToken;
        TokenProvider.XsrfToken = InitialState?.XsrfToken;

        return base.OnInitializedAsync();
    }
}

Observação

Uma alternativa para atribuir o estado inicial ao TokenProvider no exemplo anterior é copiar os dados em um serviço com escopo dentro do OnInitializedAsync para uso em todo o aplicativo.

Adicione uma referência de pacote à aplicação para o pacote NuGet Microsoft.AspNet.WebApi.Client.

Observação

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

No serviço que faz uma solicitação de API segura, injete o provedor de token e recupere o token para a solicitação de API:

WeatherForecastService.cs:

using System;
using System.Net.Http;
using System.Threading.Tasks;

public class WeatherForecastService
{
    private readonly HttpClient http;
    private readonly TokenProvider tokenProvider;

    public WeatherForecastService(IHttpClientFactory clientFactory, 
        TokenProvider tokenProvider)
    {
        http = clientFactory.CreateClient();
        this.tokenProvider = tokenProvider;
    }

    public async Task<WeatherForecast[]> GetForecastAsync()
    {
        var token = tokenProvider.AccessToken;
        using var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://localhost:5003/WeatherForecast");
        request.Headers.Add("Authorization", $"Bearer {token}");
        using var response = await http.SendAsync(request);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ?? 
            Array.Empty<WeatherForecast>();
    }
}

Para um token XSRF passado para um componente, injete o TokenProvider e adicione o token XSRF à solicitação POST. O exemplo seguinte adiciona o token a um POST no endpoint de terminação de sessão. O cenário para o exemplo a seguir é que o ponto de extremidade de logout (Areas/Identity/Pages/Account/Logout.cshtml, integrado na aplicação) não especifica uma IgnoreAntiforgeryTokenAttribute (@attribute [IgnoreAntiforgeryToken]) porque executa uma ação adicional a uma operação de logout normal que deve ser protegida. O terminal requer um token XSRF válido para processar a solicitação com sucesso.

Em um componente que apresenta um botão Logout para usuários autorizados:

@inject TokenProvider TokenProvider

...

<AuthorizeView>
    <Authorized>
        <form action="/Identity/Account/Logout?returnUrl=%2F" method="post">
            <button class="nav-link btn btn-link" type="submit">Logout</button>
            <input name="__RequestVerificationToken" type="hidden" 
                value="@TokenProvider.XsrfToken">
        </form>
    </Authorized>
    <NotAuthorized>
        ...
    </NotAuthorized>
</AuthorizeView>

Definir o esquema de autenticação

Para um aplicativo que usa mais de um middleware de autenticação e, portanto, tem mais de um esquema de autenticação, o esquema que Blazor usa pode ser explicitamente definido na configuração de ponto de extremidade do arquivo Program. O exemplo a seguir define o esquema OpenID Connect (OIDC):

Para uma aplicação que utiliza mais de um middleware de autenticação e, portanto, tem mais de um esquema de autenticação, o esquema que Blazor usa pode ser explicitamente definido na configuração do ponto final de Startup.cs. O exemplo a seguir define o esquema OpenID Connect (OIDC):

using Microsoft.AspNetCore.Authentication.OpenIdConnect;

...

app.MapRazorComponents<App>().RequireAuthorization(
    new AuthorizeAttribute
    {
        AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme
    })
    .AddInteractiveServerRenderMode();
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

...

app.MapBlazorHub().RequireAuthorization(
    new AuthorizeAttribute 
    {
        AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme
    });

Para uma aplicação que utiliza mais de um middleware de autenticação e, portanto, tem mais de um esquema de autenticação, o esquema que Blazor usa pode ser explicitamente definido na configuração do ponto final de Startup.Configure. O exemplo a seguir define o esquema de ID do Microsoft Entra:

endpoints.MapBlazorHub().RequireAuthorization(
    new AuthorizeAttribute 
    {
        AuthenticationSchemes = AzureADDefaults.AuthenticationScheme
    });

Utilizar interfaces OpenID Connect (OIDC) v2.0

Em versões do ASP.NET Core anteriores ao .NET 5, a biblioteca de autenticação e os modelos Blazor usam pontos de extremidade OpenID Connect (OIDC) v1.0. Para usar um endpoint v2.0 com versões do ASP.NET Core anteriores ao .NET 5, defina a OpenIdConnectOptions.Authority opção no OpenIdConnectOptions:

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, 
    options =>
    {
        options.Authority += "/v2.0";
    }

Como alternativa, a configuração pode ser feita no arquivo de configurações do aplicativo (appsettings.json):

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/common/oauth2/v2.0",
    ...
  }
}

Se adicionar um segmento à autoridade não for apropriado para o provedor OIDC da aplicação, como acontece com provedores que não sãoME-ID, configure a propriedade Authority diretamente. Defina a propriedade no OpenIdConnectOptions ou no arquivo de configurações do aplicativo com a chave Authority.

Alterações de código

  • A lista de declarações no token de ID muda nos endpoints da versão 2.0. A documentação da Microsoft sobre as alterações foi desativada, mas a orientação sobre as reivindicações em um token de ID está disponível na referência de reivindicações de token de ID .

  • Como os recursos são especificados em URIs com escopo para endpoints v2.0, remova a configuração da propriedade OpenIdConnectOptions.Resource em OpenIdConnectOptions:

    services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => 
        {
            ...
            options.Resource = "...";    // REMOVE THIS LINE
            ...
        }
    

URI da ID do aplicativo

  • Ao usar endpoints v2.0, as APIs definem um App ID URI, que representa um identificador exclusivo para a API.
  • Todos os escopos incluem o URI da ID do Aplicativo como um prefixo, e os pontos de extremidade v2.0 emitem tokens de acesso com o URI da ID do Aplicativo como público-alvo.
  • Ao utilizar os endpoints V2.0, a ID da cliente configurada na API do Servidor altera-se da ID da Aplicação da API (ID da Cliente) para o URI da ID da Aplicação.

appsettings.json:

{
  "AzureAd": {
    ...
    "ClientId": "https://{TENANT}.onmicrosoft.com/{PROJECT NAME}"
    ...
  }
}

Você pode encontrar o URI da ID da aplicação para usar na descrição do registo do aplicativo do fornecedor OIDC.

Manipulador de circuitos para capturar usuários para serviços personalizados

Use um CircuitHandler para capturar um usuário do AuthenticationStateProvider e definir o usuário em um serviço. Se você quiser atualizar o usuário, registre um retorno de chamada para AuthenticationStateChanged e enfileire um Task para obter o novo usuário e atualizar o serviço. O exemplo a seguir demonstra a abordagem.

No exemplo a seguir:

  • OnConnectionUpAsync é chamado toda vez que o circuito se reconecta, definindo o usuário para o tempo de vida da conexão. Somente o método OnConnectionUpAsync é necessário, a menos que você implemente atualizações por meio de um manipulador para alterações de autenticação (AuthenticationChanged no exemplo a seguir).
  • OnCircuitOpenedAsync é chamado a anexar o manipulador de alteração de autenticação AuthenticationChangedpara atualizar o usuário.
  • O bloco catch da tarefa UpdateAuthentication não executa nenhuma ação sobre exceções porque não há como relatar as exceções neste ponto da execução do código. Se uma exceção for lançada da tarefa, a exceção será comunicada em outro lugar na aplicação.

UserService.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class UserService
{
    private ClaimsPrincipal currentUser = new(new ClaimsIdentity());

    public ClaimsPrincipal GetUser() => currentUser;

    internal void SetUser(ClaimsPrincipal user)
    {
        if (currentUser != user)
        {
            currentUser = user;
        }
    }
}

internal sealed class UserCircuitHandler(
        AuthenticationStateProvider authenticationStateProvider,
        UserService userService) 
        : CircuitHandler, IDisposable
{
    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        authenticationStateProvider.AuthenticationStateChanged += 
            AuthenticationChanged;

        return base.OnCircuitOpenedAsync(circuit, cancellationToken);
    }

    private void AuthenticationChanged(Task<AuthenticationState> task)
    {
        _ = UpdateAuthentication(task);

        async Task UpdateAuthentication(Task<AuthenticationState> task)
        {
            try
            {
                var state = await task;
                userService.SetUser(state.User);
            }
            catch
            {
            }
        }
    }

    public override async Task OnConnectionUpAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        var state = await authenticationStateProvider.GetAuthenticationStateAsync();
        userService.SetUser(state.User);
    }

    public void Dispose()
    {
        authenticationStateProvider.AuthenticationStateChanged -= 
            AuthenticationChanged;
    }
}
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class UserService
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public ClaimsPrincipal GetUser()
    {
        return currentUser;
    }

    internal void SetUser(ClaimsPrincipal user)
    {
        if (currentUser != user)
        {
            currentUser = user;
        }
    }
}

internal sealed class UserCircuitHandler : CircuitHandler, IDisposable
{
    private readonly AuthenticationStateProvider authenticationStateProvider;
    private readonly UserService userService;

    public UserCircuitHandler(
        AuthenticationStateProvider authenticationStateProvider,
        UserService userService)
    {
        this.authenticationStateProvider = authenticationStateProvider;
        this.userService = userService;
    }

    public override Task OnCircuitOpenedAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        authenticationStateProvider.AuthenticationStateChanged += 
            AuthenticationChanged;

        return base.OnCircuitOpenedAsync(circuit, cancellationToken);
    }

    private void AuthenticationChanged(Task<AuthenticationState> task)
    {
        _ = UpdateAuthentication(task);

        async Task UpdateAuthentication(Task<AuthenticationState> task)
        {
            try
            {
                var state = await task;
                userService.SetUser(state.User);
            }
            catch
            {
            }
        }
    }

    public override async Task OnConnectionUpAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        var state = await authenticationStateProvider.GetAuthenticationStateAsync();
        userService.SetUser(state.User);
    }

    public void Dispose()
    {
        authenticationStateProvider.AuthenticationStateChanged -= 
            AuthenticationChanged;
    }
}

No ficheiro Program:

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;

...

builder.Services.AddScoped<UserService>();
builder.Services.TryAddEnumerable(
    ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());

Em Startup.ConfigureServices de Startup.cs:

using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection.Extensions;

...

services.AddScoped<UserService>();
services.TryAddEnumerable(
    ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());

Use o serviço num/a componente para obter o utilizador:

@inject UserService UserService

<h1>Hello, @(UserService.GetUser().Identity?.Name ?? "world")!</h1>

Para definir o utilizador no middleware para MVC, Razor Pages e em outros cenários ASP.NET Core, chame SetUser no UserService do middleware personalizado após a execução do Middleware de Autenticação ou defina o utilizador com uma implementação IClaimsTransformation. O exemplo a seguir adota a abordagem de middleware.

UserServiceMiddleware.cs:

public class UserServiceMiddleware
{
    private readonly RequestDelegate next;

    public UserServiceMiddleware(RequestDelegate next)
    {
        this.next = next ?? throw new ArgumentNullException(nameof(next));
    }

    public async Task InvokeAsync(HttpContext context, UserService service)
    {
        service.SetUser(context.User);
        await next(context);
    }
}

Imediatamente antes da chamada para app.MapRazorComponents<App>() no arquivo Program, chame o middleware:

Imediatamente antes da chamada para app.MapBlazorHub() no arquivo Program, chame o middleware:

Imediatamente antes da chamada para app.MapBlazorHub() em Startup.Configure de Startup.cs, chame o middleware:

app.UseMiddleware<UserServiceMiddleware>();

Acesso AuthenticationStateProvider no middleware de solicitação de saída

O AuthenticationStateProvider de um DelegatingHandler para HttpClient criado com IHttpClientFactory pode ser acessado no middleware de solicitação de saída usando um manipulador de atividade de circuito .

Observação

Para obter orientação geral sobre como definir manipuladores de delegação para solicitações HTTP por instâncias HttpClient criadas usando IHttpClientFactory em aplicativos do ASP.NET Core, consulte as seguintes seções de Fazer solicitações HTTP usando IHttpClientFactory no ASP.NET Core:

O exemplo a seguir usa AuthenticationStateProvider para anexar um cabeçalho de nome de usuário personalizado para usuários autenticados a solicitações de saída.

Primeiro, implemente a classe CircuitServicesAccessor na seguinte secção do artigo sobre injeção de dependência (DI) de Blazor.

Aceder a serviços do lado do servidor Blazor a partir de um escopo de DI diferente

Use o CircuitServicesAccessor para acessar o AuthenticationStateProvider na implementação DelegatingHandler.

AuthenticationStateHandler.cs:

using Microsoft.AspNetCore.Components.Authorization;

public class AuthenticationStateHandler(
    CircuitServicesAccessor circuitServicesAccessor) 
    : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authStateProvider = circuitServicesAccessor.Services?
            .GetRequiredService<AuthenticationStateProvider>();

        if (authStateProvider is null)
        {
            throw new Exception("AuthenticationStateProvider not available");
        }

        var authState = await authStateProvider.GetAuthenticationStateAsync();

        var user = authState?.User;

        if (user?.Identity is not null && user.Identity.IsAuthenticated)
        {
            request.Headers.Add("X-USER-IDENTITY-NAME", user.Identity.Name);
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

No ficheiro Program, registe o AuthenticationStateHandler e adicione o handler ao IHttpClientFactory que cria instâncias do HttpClient.

builder.Services.AddTransient<AuthenticationStateHandler>();

builder.Services.AddHttpClient("HttpMessageHandler")
    .AddHttpMessageHandler<AuthenticationStateHandler>();