Comparteix a través de


Autenticación y autorización de ASP.NET Core Blazor Hybrid

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulta la versión .NET 8 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

En este artículo se describe la compatibilidad de ASP.NET Core con la configuración y administración de la seguridad y de Identity en ASP.NET Core en las aplicaciones de Blazor Hybrid.

La autenticación en aplicaciones Blazor Hybrid se controla mediante bibliotecas de plataforma nativa, ya que ofrecen mejores garantías de seguridad que las que puede ofrecer el espacio aislado del explorador. La autenticación de aplicaciones nativas usa un mecanismo específico del sistema operativo o un protocolo federado, como OpenID Connect (OIDC). Sigue la guía del proveedor de identity que has seleccionado para la aplicación y, luego, integra adicionalmente la identity con Blazor mediante la guía de este artículo.

La integración de la autenticación debe lograr los siguientes objetivos en los componentes y servicios Razor:

  • Use las abstracciones del paquete Microsoft.AspNetCore.Components.Authorization, como AuthorizeView.
  • Reaccione a los cambios en el contexto de la autenticación.
  • Accede a las credenciales aprovisionadas por la aplicación desde el proveedor de identity, como los tokens de acceso para realizar llamadas API autorizadas.

Después de agregar la autenticación a una aplicación de .NET MAUI, WPF o Windows Forms y de que los usuarios pueden iniciar y cerrar sesión correctamente, integre la autenticación con Blazor para que el usuario autenticado esté disponible para componentes y servicios de Razor. Lleve a cabo los siguiente pasos:

Las aplicaciones de .NET MAUI usan Xamarin.Essentials: Web Authenticator: la clase WebAuthenticator permite a la aplicación iniciar flujos de autenticación basados en explorador que escuchan una devolución de llamada a una dirección URL específica registrada con la aplicación.

Para ver documentación adicional, consulte los siguientes recursos:

Las aplicaciones de Windows Forms usan la plataforma de identity de Microsoft para la integración con Microsoft Entra (ME-ID) y AAD B2C. Para más información, consulte Introducción a la Biblioteca de autenticación de Microsoft (MSAL).

Creación de un objeto AuthenticationStateProvider personalizado sin actualizaciones de cambios de usuario

Si la aplicación autentica al usuario inmediatamente después de iniciarse y el usuario autenticado sigue siendo el mismo mientras dura la aplicación, no se necesitan notificaciones de cambio de usuario; la aplicación solo proporciona información sobre el usuario autenticado. En este escenario, el usuario inicia sesión en la aplicación cuando esta se abre y la aplicación vuelve a mostrar la pantalla de inicio de sesión después de que el usuario cierra sesión. El siguiente elemento ExternalAuthStateProvider es una implementación de ejemplo de un objeto AuthenticationStateProvider personalizado para este escenario de autenticación.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.
  • Devuelve el host compilado.

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que devuelve un objeto Microsoft.Maui.Hosting.MauiApp compilado:

- return builder.Build();

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios y agregue la colección de servicios compilada como un recurso al objeto ResourceDictionary de la aplicación.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.
  • Devuelve el host compilado.

En el constructor de MainWindow (MainWindow.xaml.cs), agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que agrega la colección de servicios compilada como un recurso al objeto ResourceDictionary de la aplicación:

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

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios y agregue la colección de servicios compilada al proveedor de servicios de la aplicación.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.

En el constructor de Form1 (Form1.cs), agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que establece la colección de servicios compilada en el proveedor de servicios de la aplicación:

- blazorWebView1.Services = services.BuildServiceProvider();

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Creación de un objeto AuthenticationStateProvider personalizado con actualizaciones de cambios de usuario

Para actualizar el usuario mientras se ejecuta la aplicación Blazor, llame a NotifyAuthenticationStateChanged dentro de la implementación de AuthenticationStateProvider mediante cualquiera de los métodos siguientes:

Señalización de una actualización de autenticación desde fuera de BlazorWebView (Opción 1)

Un objeto AuthenticationStateProvider personalizado puede usar un servicio global para indicar una actualización de autenticación. Se recomienda que el servicio ofrezca un evento al que AuthenticationStateProvider pueda suscribirse, donde el evento invoca NotifyAuthenticationStateChanged.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

En el constructor de MainWindow (MainWindow.xaml.cs), agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

En el constructor de Form1 (Form1.cs), agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

Siempre que la aplicación autentique a un usuario, resuelva el servicio ExternalAuthService:

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

Ejecute el código de OpenID o MSAL personalizado para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity. El usuario autenticado (authenticatedUser en el ejemplo siguiente) es un nuevo objeto ClaimsPrincipal basado en un nuevo objeto ClaimsIdentity.

Establezca el usuario actual en el usuario autenticado:

authService.CurrentUser = authenticatedUser;

Una alternativa al enfoque anterior es establecer la entidad de seguridad del usuario en System.Threading.Thread.CurrentPrincipal en lugar de establecerla a través de un servicio, lo que evita el uso del contenedor de inserción de dependencias:

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

Con el enfoque alternativo, solo se agregan los servicios de autorización (AddAuthorizationCore) y CurrentThreadUserAuthenticationStateProvider (.TryAddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) a la colección de servicios.

Control de la autenticación dentro de BlazorWebView (Opción 2)

Un objeto AuthenticationStateProvider personalizado puede incluir métodos adicionales para desencadenar el inicio y cierre de sesión y actualizar el usuario.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

En el ejemplo anterior:

  • La llamada a LogInAsyncCore desencadena el proceso de inicio de sesión.
  • La llamada a NotifyAuthenticationStateChanged notifica que hay una actualización en curso, que permite a la aplicación proporcionar una interfaz de usuario temporal durante el proceso de inicio o cierre de sesión.
  • Al devolver loginTask se devuelve la tarea para que el componente que desencadenó el inicio de sesión pueda esperar y reaccionar una vez finalizada esta.
  • El desarrollador implementa el método LoginWithExternalProviderAsync para iniciar sesión en el usuario con el SDK del proveedor de identity. Para más información, consulta la documentación del proveedor de identity. El usuario autenticado (authenticatedUser) es un nuevo objeto ClaimsPrincipal basado en un objeto ClaimsIdentity nuevo.

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el constructor de MainWindow (MainWindow.xaml.cs), agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el constructor de Form1 (Form1.cs), agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el componente LoginComponent siguiente se muestra cómo iniciar la sesión de un usuario. En una aplicación típica, el componente LoginComponent solo se muestra en un componente primario si el usuario no ha iniciado sesión en la aplicación.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

En el componente LogoutComponent siguiente se muestra cómo cerrar la sesión de un usuario. En una aplicación típica, el componente LogoutComponent solo se muestra en un componente primario si se ha iniciado la sesión del usuario en la aplicación.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Acceso a otra información de autenticación

Blazor no define una abstracción para ocuparse de otras credenciales, como los tokens de acceso que se usarán para las solicitudes HTTP a las API web. Se recomienda seguir la guía del proveedor de identity para administrar las credenciales del usuario con los primitivos que proporciona el SDK del proveedor de identity.

Es habitual que los SDK del proveedor de identity usen un almacén de tokens para las credenciales de usuario almacenadas en el dispositivo. Si el primitivo del almacén de tokens del SDK se agrega al contenedor de servicios, consuma el primitivo del SDK dentro de la aplicación.

El marco de Blazor no conoce las credenciales de autenticación de un usuario y no interactúa con las credenciales de ninguna manera, por lo que el código de la aplicación es libre de seguir el enfoque que considere más conveniente. Sin embargo, siga la guía general de seguridad de la sección siguiente, Otras consideraciones sobre la seguridad de la autenticación, al implementar el código de autenticación en una aplicación.

Otras consideraciones sobre la seguridad de la autenticación

El proceso de autenticación es externo a Blazor y se recomienda que los desarrolladores accedan a la guía del proveedor de identity para obtener información de seguridad adicional.

Al implementar la autenticación:

  • Evite la autenticación en el contexto de Web View. Por ejemplo, evite usar una biblioteca de OAuth de JavaScript para realizar el flujo de autenticación. En una aplicación de página única, los tokens de autenticación no están ocultos en JavaScript y los usuarios malintencionados pueden detectarlos fácilmente y usarlos con fines malvados. Las aplicaciones nativas no sufren este riesgo porque solo pueden obtener tokens fuera del contexto del explorador, lo que significa que los scripts de terceros no autorizados no pueden robar los tokens y poner en peligro la aplicación.
  • Evite implementar el flujo de trabajo de autenticación por su cuenta. En la mayoría de los casos, las bibliotecas de plataforma controlan de forma segura el flujo de trabajo de autenticación, mediante el explorador del sistema en lugar de usar un objeto Web View personalizado que se puede secuestrar.
  • Evite usar el control Web View de la plataforma para realizar la autenticación. En su lugar, confíe en el explorador del sistema cuando sea posible.
  • Evite pasar los tokens al contexto del documento (JavaScript). En algunas situaciones, se requiere una biblioteca de JavaScript en el documento para realizar una llamada autorizada a un servicio externo. En lugar de hacer que el token esté disponible para JavaScript a través de la interoperabilidad de JS:
    • Proporcione un token temporal generado a la biblioteca y dentro de Web View.
    • Intercepte la solicitud de red saliente en el código.
    • Reemplace el token temporal por el token real y confirme que el destino de la solicitud es válido.

Recursos adicionales

La autenticación en aplicaciones Blazor Hybrid se controla mediante bibliotecas de plataforma nativa, ya que ofrecen mejores garantías de seguridad que las que puede ofrecer el espacio aislado del explorador. La autenticación de aplicaciones nativas usa un mecanismo específico del sistema operativo o un protocolo federado, como OpenID Connect (OIDC). Sigue la guía del proveedor de identity que has seleccionado para la aplicación y, luego, integra adicionalmente la identity con Blazor mediante la guía de este artículo.

La integración de la autenticación debe lograr los siguientes objetivos en los componentes y servicios Razor:

  • Use las abstracciones del paquete Microsoft.AspNetCore.Components.Authorization, como AuthorizeView.
  • Reaccione a los cambios en el contexto de la autenticación.
  • Accede a las credenciales aprovisionadas por la aplicación desde el proveedor de identity, como los tokens de acceso para realizar llamadas API autorizadas.

Después de agregar la autenticación a una aplicación de .NET MAUI, WPF o Windows Forms y de que los usuarios pueden iniciar y cerrar sesión correctamente, integre la autenticación con Blazor para que el usuario autenticado esté disponible para componentes y servicios de Razor. Lleve a cabo los siguiente pasos:

Las aplicaciones de .NET MAUI usan Xamarin.Essentials: Web Authenticator: la clase WebAuthenticator permite a la aplicación iniciar flujos de autenticación basados en explorador que escuchan una devolución de llamada a una dirección URL específica registrada con la aplicación.

Para ver documentación adicional, consulte los siguientes recursos:

Las aplicaciones de Windows Forms usan la plataforma de identity de Microsoft para la integración con Microsoft Entra (ME-ID) y AAD B2C. Para más información, consulte Introducción a la Biblioteca de autenticación de Microsoft (MSAL).

Creación de un objeto AuthenticationStateProvider personalizado sin actualizaciones de cambios de usuario

Si la aplicación autentica al usuario inmediatamente después de iniciarse y el usuario autenticado sigue siendo el mismo mientras dura la aplicación, no se necesitan notificaciones de cambio de usuario; la aplicación solo proporciona información sobre el usuario autenticado. En este escenario, el usuario inicia sesión en la aplicación cuando esta se abre y la aplicación vuelve a mostrar la pantalla de inicio de sesión después de que el usuario cierra sesión. El siguiente elemento ExternalAuthStateProvider es una implementación de ejemplo de un objeto AuthenticationStateProvider personalizado para este escenario de autenticación.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.
  • Devuelve el host compilado.

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que devuelve un objeto Microsoft.Maui.Hosting.MauiApp compilado:

- return builder.Build();

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios y agregue la colección de servicios compilada como un recurso al objeto ResourceDictionary de la aplicación.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.
  • Devuelve el host compilado.

En el constructor de MainWindow (MainWindow.xaml.cs), agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que agrega la colección de servicios compilada como un recurso al objeto ResourceDictionary de la aplicación:

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

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Los pasos siguientes describen cómo hacerlo:

  • Agregue los espacios de nombres necesarios.
  • Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios.
  • Compile la colección de servicios y agregue la colección de servicios compilada al proveedor de servicios de la aplicación.
  • Resuelva el servicio AuthenticatedUser para establecer la entidad de notificaciones del usuario autenticado. Para más información, consulta la documentación del proveedor de identity.

En el constructor de Form1 (Form1.cs), agregue espacios de nombres para Microsoft.AspNetCore.Components.Authorization y System.Security.Claims:

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

Quite la siguiente línea de código que establece la colección de servicios compilada en el proveedor de servicios de la aplicación:

- blazorWebView1.Services = services.BuildServiceProvider();

Reemplace la línea de código anterior por el código siguiente. Agregue código de OpenID o MSAL para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity.

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;

Creación de un objeto AuthenticationStateProvider personalizado con actualizaciones de cambios de usuario

Para actualizar el usuario mientras se ejecuta la aplicación Blazor, llame a NotifyAuthenticationStateChanged dentro de la implementación de AuthenticationStateProvider mediante cualquiera de los métodos siguientes:

Señalización de una actualización de autenticación desde fuera de BlazorWebView (Opción 1)

Un objeto AuthenticationStateProvider personalizado puede usar un servicio global para indicar una actualización de autenticación. Se recomienda que el servicio ofrezca un evento al que AuthenticationStateProvider pueda suscribirse, donde el evento invoca NotifyAuthenticationStateChanged.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

En el constructor de MainWindow (MainWindow.xaml.cs), agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

En el constructor de Form1 (Form1.cs), agregue un espacio de nombres para Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Agregue los servicios de autorización y las abstracciones de Blazor a la colección de servicios:

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

Siempre que la aplicación autentique a un usuario, resuelva el servicio ExternalAuthService:

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

Ejecute el código de OpenID o MSAL personalizado para autenticar al usuario. Para más información, consulta la documentación del proveedor de identity. El usuario autenticado (authenticatedUser en el ejemplo siguiente) es un nuevo objeto ClaimsPrincipal basado en un nuevo objeto ClaimsIdentity.

Establezca el usuario actual en el usuario autenticado:

authService.CurrentUser = authenticatedUser;

Una alternativa al enfoque anterior es establecer la entidad de seguridad del usuario en System.Threading.Thread.CurrentPrincipal en lugar de establecerla a través de un servicio, lo que evita el uso del contenedor de inserción de dependencias:

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

Con el enfoque alternativo, solo se agregan los servicios de autorización (AddAuthorizationCore) y CurrentThreadUserAuthenticationStateProvider (.AddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) a la colección de servicios.

Control de la autenticación dentro de BlazorWebView (Opción 2)

Un objeto AuthenticationStateProvider personalizado puede incluir métodos adicionales para desencadenar el inicio y cierre de sesión y actualizar el usuario.

Nota

El siguiente objeto AuthenticationStateProvider personalizado no declara un espacio de nombres para que el ejemplo de código sea aplicable a cualquier aplicación de Blazor Hybrid. Sin embargo, un procedimiento recomendado es proporcionar el espacio de nombres de la aplicación al implementar el ejemplo en una aplicación de producción.

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

En el ejemplo anterior:

  • La llamada a LogInAsyncCore desencadena el proceso de inicio de sesión.
  • La llamada a NotifyAuthenticationStateChanged notifica que hay una actualización en curso, que permite a la aplicación proporcionar una interfaz de usuario temporal durante el proceso de inicio o cierre de sesión.
  • Al devolver loginTask se devuelve la tarea para que el componente que desencadenó el inicio de sesión pueda esperar y reaccionar una vez finalizada esta.
  • El desarrollador implementa el método LoginWithExternalProviderAsync para iniciar sesión en el usuario con el SDK del proveedor de identity. Para más información, consulta la documentación del proveedor de identity. El usuario autenticado (authenticatedUser) es un nuevo objeto ClaimsPrincipal basado en un objeto ClaimsIdentity nuevo.

En el método MauiProgram.CreateMauiApp de MauiProgram.cs, agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el constructor de MainWindow (MainWindow.xaml.cs), agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el constructor de Form1 (Form1.cs), agregue los servicios de autorización y la abstracción de Blazor a la colección de servicios:

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

En el componente LoginComponent siguiente se muestra cómo iniciar la sesión de un usuario. En una aplicación típica, el componente LoginComponent solo se muestra en un componente primario si el usuario no ha iniciado sesión en la aplicación.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

En el componente LogoutComponent siguiente se muestra cómo cerrar la sesión de un usuario. En una aplicación típica, el componente LogoutComponent solo se muestra en un componente primario si se ha iniciado la sesión del usuario en la aplicación.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Acceso a otra información de autenticación

Blazor no define una abstracción para ocuparse de otras credenciales, como los tokens de acceso que se usarán para las solicitudes HTTP a las API web. Se recomienda seguir la guía del proveedor de identity para administrar las credenciales del usuario con los primitivos que proporciona el SDK del proveedor de identity.

Es habitual que los SDK del proveedor de identity usen un almacén de tokens para las credenciales de usuario almacenadas en el dispositivo. Si el primitivo del almacén de tokens del SDK se agrega al contenedor de servicios, consuma el primitivo del SDK dentro de la aplicación.

El marco de Blazor no conoce las credenciales de autenticación de un usuario y no interactúa con las credenciales de ninguna manera, por lo que el código de la aplicación es libre de seguir el enfoque que considere más conveniente. Sin embargo, siga la guía general de seguridad de la sección siguiente, Otras consideraciones sobre la seguridad de la autenticación, al implementar el código de autenticación en una aplicación.

Otras consideraciones sobre la seguridad de la autenticación

El proceso de autenticación es externo a Blazor y se recomienda que los desarrolladores accedan a la guía del proveedor de identity para obtener información de seguridad adicional.

Al implementar la autenticación:

  • Evite la autenticación en el contexto de Web View. Por ejemplo, evite usar una biblioteca de OAuth de JavaScript para realizar el flujo de autenticación. En una aplicación de página única, los tokens de autenticación no están ocultos en JavaScript y los usuarios malintencionados pueden detectarlos fácilmente y usarlos con fines malvados. Las aplicaciones nativas no sufren este riesgo porque solo pueden obtener tokens fuera del contexto del explorador, lo que significa que los scripts de terceros no autorizados no pueden robar los tokens y poner en peligro la aplicación.
  • Evite implementar el flujo de trabajo de autenticación por su cuenta. En la mayoría de los casos, las bibliotecas de plataforma controlan de forma segura el flujo de trabajo de autenticación, mediante el explorador del sistema en lugar de usar un objeto Web View personalizado que se puede secuestrar.
  • Evite usar el control Web View de la plataforma para realizar la autenticación. En su lugar, confíe en el explorador del sistema cuando sea posible.
  • Evite pasar los tokens al contexto del documento (JavaScript). En algunas situaciones, se requiere una biblioteca de JavaScript en el documento para realizar una llamada autorizada a un servicio externo. En lugar de hacer que el token esté disponible para JavaScript a través de la interoperabilidad de JS:
    • Proporcione un token temporal generado a la biblioteca y dentro de Web View.
    • Intercepte la solicitud de red saliente en el código.
    • Reemplace el token temporal por el token real y confirme que el destino de la solicitud es válido.

Recursos adicionales