Share via


Autenticação e autorização do Blazor Hybrid no ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

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

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Este artigo descreve o suporte do ASP.NET Core para a configuração e o gerenciamento de segurança em aplicativos Identity e Blazor Hybrid do ASP.NET Core.

A autenticação em aplicativos Blazor Hybrid é tratada por bibliotecas de plataforma nativas, pois elas oferecem garantias de segurança aprimoradas que a área restrita do navegador não pode oferecer. A autenticação de aplicativos nativos usa um mecanismo específico do sistema operacional ou um protocolo federado, como o OIDC (OpenID Connect). Siga as diretrizes para o provedor de identidade que você selecionou para o aplicativo e integre ainda mais a identidade com Blazor usando as diretrizes neste artigo.

A integração da autenticação deve atingir as seguintes metas para componentes e serviços do Razor:

  • Usar as abstrações no pacote Microsoft.AspNetCore.Components.Authorization, como AuthorizeView.
  • Reagir a alterações no contexto da autenticação.
  • Acessar as credenciais provisionadas pelo aplicativo do provedor de identidade, como tokens de acesso, para executar chamadas à API autorizadas.

Após a autenticação ser adicionada a um aplicativo .NET MAUI, WPF ou Windows Forms e os usuários poderem fazer logon e logoff com êxito, integrar a autenticação com Blazor para disponibilizar o usuário autenticado para componentes e serviços do Razor. Execute as seguintes etapas:

As aplicativos .NET MAUI usam o Xamarin.Essentials: Web Authenticator: a classe WebAuthenticator permite que o aplicativo inicie fluxos de autenticação baseados em navegador que escutam um retorno de chamada para uma URL específica registrada com o aplicativo.

Para orientações adicionais, confira os seguintes recursos:

Os aplicativos do Windows Forms usam a plataforma de identidade da Microsoft para se integrar ao Microsoft Entra (ME-ID) e ao AAD B2C. Para obter mais informações, confira Visão geral da a Biblioteca de Autenticação da Microsoft (MSAL).

Criar um AuthenticationStateProvider personalizado sem atualizações de alteração do usuário

Se o aplicativo autenticar o usuário imediatamente após sua inicialização e o usuário autenticado permanecer o mesmo durante todo o tempo de vida do aplicativo, notificações de alteração do usuário não serão necessárias e o aplicativo fornecerá apenas informações sobre o usuário autenticado. Nesse cenário, o usuário faz logon no aplicativo quando ele é aberto, e o aplicativo exibe a tela de logon novamente depois que o usuário faz logon. O ExternalAuthStateProvider a seguir é um exemplo de implementação de um AuthenticationStateProvider personalizado para esse cenário de autenticação.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Criar a coleção de serviços.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.
  • Retornar o host criado.

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione namespaces para Microsoft.AspNetCore.Components.Authorization eSystem.Security.Claims:

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

Remova a seguinte linha de código que retorna um Microsoft.Maui.Hosting.MauiApp compilado:

- return builder.Build();

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Crie a coleção de serviços e adicione a coleção de serviços criada como um recurso ao ResourceDictionary do aplicativo.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.
  • Retornar o host criado.

No construtor do MainWindow (MainWindow.xaml.cs), adicione namespaces para Microsoft.AspNetCore.Components.Authorization e System.Security.Claims:

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

Remova a seguinte linha de código que adiciona a coleção de serviços criada como um recurso ao ResourceDictionary do aplicativo:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Crie a coleção de serviços e adicione a coleção de serviços criada ao provedor de serviço do aplicativo.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.

No construtor do Form1 (Form1.cs), adicione namespaces para Microsoft.AspNetCore.Components.Authorization e System.Security.Claims:

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

Remova a seguinte linha de código que define a coleção de serviços criada para o provedor de serviços do aplicativo:

- blazorWebView1.Services = services.BuildServiceProvider();

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Criar um AuthenticationStateProvider personalizado com atualizações de alteração do usuário

Para atualizar o usuário enquanto o aplicativo Blazor está em execução, chame NotifyAuthenticationStateChanged dentro da implementação de AuthenticationStateProvider usando uma das seguintes abordagens:

Sinalizar uma atualização de autenticação de fora do BlazorWebView (opção 1)

Um AuthenticationStateProvider personalizado pode usar um serviço global para sinalizar uma atualização de autenticação. Recomendamos que o serviço ofereça um evento que AuthenticationStateProvider possa assinar, em que o evento invoca NotifyAuthenticationStateChanged.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

No MainWindow do construtor (MainWindow.xaml.cs), adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e as abstrações de Blazor à coleção de serviços:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

No Form1 do construtor (Form1.cs), adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Onde quer que o aplicativo autentique um usuário, resolva o serviço ExternalAuthService:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Execute o código OpenID/MSAL personalizado para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes. O usuário autenticado (authenticatedUser no exemplo a seguir) é um novo ClaimsPrincipal baseado em um novo ClaimsIdentity.

Defina o usuário atual para o usuário autenticado:

authService.CurrentUser = authenticatedUser;

Uma alternativa à abordagem anterior é definir a entidade de segurança do usuário System.Threading.Thread.CurrentPrincipal em vez de defini-la por meio de um serviço, o que evita o uso do contêiner de injeção de dependência:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

Usando a abordagem alternativa, somente os serviços de autorização (AddAuthorizationCore) e CurrentThreadUserAuthenticationStateProvider (.TryAddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) são adicionados à coleção de serviços.

Manipular a autenticação dentro do BlazorWebView (opção 2)

Um AuthenticationStateProvider personalizado pode incluir métodos adicionais para disparar o logon e o logoff e atualizar o usuário.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

No exemplo anterior:

  • A chamada para LogInAsyncCore dispara o processo de logon.
  • A chamada para NotifyAuthenticationStateChanged notifica que uma atualização está em andamento, o que permite que o aplicativo forneça uma interface do usuário temporária durante o processo de logoon ou logoff.
  • Retornar loginTask retorna a tarefa de modo que o componente que disparou o logon possa aguardar e reagir após a conclusão da tarefa.
  • O método LoginWithExternalProviderAsync é implementado pelo desenvolvedor para fazer logon no usuário com o SDK do provedor de identidade. Para obter mais informações, consulte a documentação do seu provedor de identidade. O usuário autenticado (authenticatedUser) é um novo ClaimsPrincipal baseado em um novo ClaimsIdentity.

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

No construtor de MainWindow (MainWindow.xaml.cs), adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

No construtor de Form1 (Form1.cs), adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

O componente LoginComponent a seguir demonstra como fazer logon em um usuário. Em um aplicativo típico, o componente LoginComponent só será mostrado em um componente pai se o usuário não estiver conectado ao aplicativo.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

O componente LogoutComponent a seguir demonstra como fazer logoff de um usuário. Em um aplicativo típico, o componente LogoutComponent só será mostrado em um componente pai se o usuário estiver conectado ao aplicativo.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Acessando outras informações de autenticação

Blazor não define uma abstração para lidar com outras credenciais, como tokens de acesso a serem usados para solicitações HTTP para APIs Web. É recomendável seguir as diretrizes do provedor de identidade para gerenciar as credenciais do usuário com os primitivos que o SDK do provedor de identidade fornece.

É comum que os SDKs do provedor de identidade usem um repositório de tokens para credenciais de usuário armazenadas no dispositivo. Se o primitivo do repositório de tokens do SDK for adicionado ao contêiner de serviço, consuma o primitivo do SDK dentro do aplicativo.

A estrutura Blazor não está ciente das credenciais de autenticação de um usuário e não interage com credenciais de forma alguma, portanto, o código do aplicativo fica livre para seguir qualquer abordagem que você considerar mais conveniente. No entanto, siga as diretrizes gerais de segurança na próxima seção, Outras considerações de segurança de autenticação, ao implementar o código de autenticação em um aplicativo.

Outras considerações de segurança de autenticação

O processo de autenticação é externo para Blazor, e recomendamos que os desenvolvedores acessem as diretrizes do provedor de identidade para obter diretrizes de segurança adicionais.

Ao implementar a autenticação:

  • Evite a autenticação no contexto do Web View. Por exemplo, evite usar uma biblioteca OAuth do JavaScript para executar o fluxo de autenticação. Em um aplicativo de página única, os tokens de autenticação não estão ocultos no JavaScript e podem ser descobertos facilmente por usuários mal-intencionados e usados para fins nefastos. Os aplicativos nativos não sofrem esse risco porque só são capazes de obter tokens fora do contexto do navegador, o que significa que scripts de terceiros não podem roubar os tokens e comprometer o aplicativo.
  • Evite implementar o fluxo de trabalho de autenticação por conta própria. Na maioria dos casos, as bibliotecas da plataforma lidam com segurança com o fluxo de trabalho de autenticação, usando o navegador do sistema em vez de usar um Web View personalizado que pode ser sequestrado.
  • Evite usar o controle Web View da plataforma para executar a autenticação. Em vez disso, use o navegador do sistema quando possível.
  • Evite passar os tokens para o contexto do documento (JavaScript). Em algumas situações, uma biblioteca JavaScript dentro do documento é necessária para executar uma chamada autorizada a um serviço externo. Em vez de disponibilizar o token para o JavaScript por meio da interoperabilidade dom JS:
    • Forneça um token temporário gerado para a biblioteca e dentro do Web View.
    • Intercepte a solicitação de rede de saída no código.
    • Substitua o token temporário pelo token real e confirme se o destino da solicitação é válido.

Recursos adicionais

A autenticação em aplicativos Blazor Hybrid é tratada por bibliotecas de plataforma nativas, pois elas oferecem garantias de segurança aprimoradas que a área restrita do navegador não pode oferecer. A autenticação de aplicativos nativos usa um mecanismo específico do sistema operacional ou um protocolo federado, como o OIDC (OpenID Connect). Siga as diretrizes para o provedor de identidade que você selecionou para o aplicativo e integre ainda mais a identidade com Blazor usando as diretrizes neste artigo.

A integração da autenticação deve atingir as seguintes metas para componentes e serviços do Razor:

  • Usar as abstrações no pacote Microsoft.AspNetCore.Components.Authorization, como AuthorizeView.
  • Reagir a alterações no contexto da autenticação.
  • Acessar as credenciais provisionadas pelo aplicativo do provedor de identidade, como tokens de acesso, para executar chamadas à API autorizadas.

Após a autenticação ser adicionada a um aplicativo .NET MAUI, WPF ou Windows Forms e os usuários poderem fazer logon e logoff com êxito, integrar a autenticação com Blazor para disponibilizar o usuário autenticado para componentes e serviços do Razor. Execute as seguintes etapas:

As aplicativos .NET MAUI usam o Xamarin.Essentials: Web Authenticator: a classe WebAuthenticator permite que o aplicativo inicie fluxos de autenticação baseados em navegador que escutam um retorno de chamada para uma URL específica registrada com o aplicativo.

Para orientações adicionais, confira os seguintes recursos:

Os aplicativos do Windows Forms usam a plataforma de identidade da Microsoft para se integrar ao Microsoft Entra (ME-ID) e ao AAD B2C. Para obter mais informações, confira Visão geral da a Biblioteca de Autenticação da Microsoft (MSAL).

Criar um AuthenticationStateProvider personalizado sem atualizações de alteração do usuário

Se o aplicativo autenticar o usuário imediatamente após sua inicialização e o usuário autenticado permanecer o mesmo durante todo o tempo de vida do aplicativo, notificações de alteração do usuário não serão necessárias e o aplicativo fornecerá apenas informações sobre o usuário autenticado. Nesse cenário, o usuário faz logon no aplicativo quando ele é aberto, e o aplicativo exibe a tela de logon novamente depois que o usuário faz logon. O ExternalAuthStateProvider a seguir é um exemplo de implementação de um AuthenticationStateProvider personalizado para esse cenário de autenticação.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Criar a coleção de serviços.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.
  • Retornar o host criado.

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione namespaces para Microsoft.AspNetCore.Components.Authorization eSystem.Security.Claims:

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

Remova a seguinte linha de código que retorna um Microsoft.Maui.Hosting.MauiApp compilado:

- return builder.Build();

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Crie a coleção de serviços e adicione a coleção de serviços criada como um recurso ao ResourceDictionary do aplicativo.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.
  • Retornar o host criado.

No construtor do MainWindow (MainWindow.xaml.cs), adicione namespaces para Microsoft.AspNetCore.Components.Authorization e System.Security.Claims:

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

Remova a seguinte linha de código que adiciona a coleção de serviços criada como um recurso ao ResourceDictionary do aplicativo:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

As seguintes etapas descrevem como:

  • Adicionar namespaces obrigatórios.
  • Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços.
  • Crie a coleção de serviços e adicione a coleção de serviços criada ao provedor de serviço do aplicativo.
  • Resolva o serviço AuthenticatedUser para definir a entidade de declaração do usuário autenticado. Consulte a documentação do provedor de identidade para obter detalhes.

No construtor do Form1 (Form1.cs), adicione namespaces para Microsoft.AspNetCore.Components.Authorization e System.Security.Claims:

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

Remova a seguinte linha de código que define a coleção de serviços criada para o provedor de serviços do aplicativo:

- blazorWebView1.Services = services.BuildServiceProvider();

Substitua a linha de código anterior pelo código a seguir. Adicione código do OpenID/MSAL para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes.

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Criar um AuthenticationStateProvider personalizado com atualizações de alteração do usuário

Para atualizar o usuário enquanto o aplicativo Blazor está em execução, chame NotifyAuthenticationStateChanged dentro da implementação de AuthenticationStateProvider usando uma das seguintes abordagens:

Sinalizar uma atualização de autenticação de fora do BlazorWebView (opção 1)

Um AuthenticationStateProvider personalizado pode usar um serviço global para sinalizar uma atualização de autenticação. Recomendamos que o serviço ofereça um evento que AuthenticationStateProvider possa assinar, em que o evento invoca NotifyAuthenticationStateChanged.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

No MainWindow do construtor (MainWindow.xaml.cs), adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e as abstrações de Blazor à coleção de serviços:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

No Form1 do construtor (Form1.cs), adicione um namespace para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Adicionar os serviços de autorização e abstrações de Blazor à coleção de serviços:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Onde quer que o aplicativo autentique um usuário, resolva o serviço ExternalAuthService:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Execute o código OpenID/MSAL personalizado para autenticar o usuário. Consulte a documentação do provedor de identidade para obter detalhes. O usuário autenticado (authenticatedUser no exemplo a seguir) é um novo ClaimsPrincipal baseado em um novo ClaimsIdentity.

Defina o usuário atual para o usuário autenticado:

authService.CurrentUser = authenticatedUser;

Uma alternativa à abordagem anterior é definir a entidade de segurança do usuário System.Threading.Thread.CurrentPrincipal em vez de defini-la por meio de um serviço, o que evita o uso do contêiner de injeção de dependência:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

Usando a abordagem alternativa, somente os serviços de autorização (AddAuthorizationCore) e CurrentThreadUserAuthenticationStateProvider (.AddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) são adicionados à coleção de serviços.

Manipular a autenticação dentro do BlazorWebView (opção 2)

Um AuthenticationStateProvider personalizado pode incluir métodos adicionais para disparar o logon e o logoff e atualizar o usuário.

Observação

O AuthenticationStateProvider personalizado a seguir não declara um namespace para tornar o exemplo de código aplicável a qualquer aplicativo Blazor Hybrid. No entanto, uma melhor prática é fornecer o namespace do aplicativo quando você implementa o exemplo em um aplicativo de produção.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

No exemplo anterior:

  • A chamada para LogInAsyncCore dispara o processo de logon.
  • A chamada para NotifyAuthenticationStateChanged notifica que uma atualização está em andamento, o que permite que o aplicativo forneça uma interface do usuário temporária durante o processo de logoon ou logoff.
  • Retornar loginTask retorna a tarefa de modo que o componente que disparou o logon possa aguardar e reagir após a conclusão da tarefa.
  • O método LoginWithExternalProviderAsync é implementado pelo desenvolvedor para fazer logon no usuário com o SDK do provedor de identidade. Para obter mais informações, consulte a documentação do seu provedor de identidade. O usuário autenticado (authenticatedUser) é um novo ClaimsPrincipal baseado em um novo ClaimsIdentity.

No método MauiProgram.CreateMauiApp de MauiProgram.cs, adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

No construtor de MainWindow (MainWindow.xaml.cs), adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

No construtor de Form1 (Form1.cs), adicione os serviços de autorização e as abstrações de Blazor à coleção de serviços:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

O componente LoginComponent a seguir demonstra como fazer logon em um usuário. Em um aplicativo típico, o componente LoginComponent só será mostrado em um componente pai se o usuário não estiver conectado ao aplicativo.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

O componente LogoutComponent a seguir demonstra como fazer logoff de um usuário. Em um aplicativo típico, o componente LogoutComponent só será mostrado em um componente pai se o usuário estiver conectado ao aplicativo.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Acessando outras informações de autenticação

Blazor não define uma abstração para lidar com outras credenciais, como tokens de acesso a serem usados para solicitações HTTP para APIs Web. É recomendável seguir as diretrizes do provedor de identidade para gerenciar as credenciais do usuário com os primitivos que o SDK do provedor de identidade fornece.

É comum que os SDKs do provedor de identidade usem um repositório de tokens para credenciais de usuário armazenadas no dispositivo. Se o primitivo do repositório de tokens do SDK for adicionado ao contêiner de serviço, consuma o primitivo do SDK dentro do aplicativo.

A estrutura Blazor não está ciente das credenciais de autenticação de um usuário e não interage com credenciais de forma alguma, portanto, o código do aplicativo fica livre para seguir qualquer abordagem que você considerar mais conveniente. No entanto, siga as diretrizes gerais de segurança na próxima seção, Outras considerações de segurança de autenticação, ao implementar o código de autenticação em um aplicativo.

Outras considerações de segurança de autenticação

O processo de autenticação é externo para Blazor, e recomendamos que os desenvolvedores acessem as diretrizes do provedor de identidade para obter diretrizes de segurança adicionais.

Ao implementar a autenticação:

  • Evite a autenticação no contexto do Web View. Por exemplo, evite usar uma biblioteca OAuth do JavaScript para executar o fluxo de autenticação. Em um aplicativo de página única, os tokens de autenticação não estão ocultos no JavaScript e podem ser descobertos facilmente por usuários mal-intencionados e usados para fins nefastos. Os aplicativos nativos não sofrem esse risco porque só são capazes de obter tokens fora do contexto do navegador, o que significa que scripts de terceiros não podem roubar os tokens e comprometer o aplicativo.
  • Evite implementar o fluxo de trabalho de autenticação por conta própria. Na maioria dos casos, as bibliotecas da plataforma lidam com segurança com o fluxo de trabalho de autenticação, usando o navegador do sistema em vez de usar um Web View personalizado que pode ser sequestrado.
  • Evite usar o controle Web View da plataforma para executar a autenticação. Em vez disso, use o navegador do sistema quando possível.
  • Evite passar os tokens para o contexto do documento (JavaScript). Em algumas situações, uma biblioteca JavaScript dentro do documento é necessária para executar uma chamada autorizada a um serviço externo. Em vez de disponibilizar o token para o JavaScript por meio da interoperabilidade dom JS:
    • Forneça um token temporário gerado para a biblioteca e dentro do Web View.
    • Intercepte a solicitação de rede de saída no código.
    • Substitua o token temporário pelo token real e confirme se o destino da solicitação é válido.

Recursos adicionais