Compartir a través de


Protección de ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 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 de .NET 9 de este artículo.

Las aplicaciones independientes Blazor WebAssembly se pueden proteger con ASP.NET Core Identity siguiendo las instrucciones de este artículo.

Puntos de conexión para registrarse, iniciar sesión y cerrar sesión

En lugar de usar la interfaz de usuario predeterminada proporcionada por ASP.NET Core Identity para SPA y aplicaciones Blazor, que se basa en páginas Razor, llama a MapIdentityApi en una API de back-end para agregar puntos de conexión de API JSON para registrar e iniciar sesión en usuarios con ASP.NET Core Identity. Los puntos de conexión de API Identity también admiten características avanzadas, como la autenticación en dos fases y la comprobación de correo electrónico.

En el cliente, llama al punto de conexión /register para registrar un usuario con su dirección de correo electrónico y contraseña:

var result = await _httpClient.PostAsJsonAsync(
    "register", new
    {
        email,
        password
    });

En el cliente, inicia sesión en un usuario con autenticación cookie mediante el punto de conexión /login con la cadena de consulta useCookies establecida en true:

var result = await _httpClient.PostAsJsonAsync(
    "login?useCookies=true", new
    {
        email,
        password
    });

La API del servidor back-end establece la autenticación cookie con una llamada a AddIdentityCookies en el generador de autenticación:

builder.Services
    .AddAuthentication(IdentityConstants.ApplicationScheme)
    .AddIdentityCookies();

Autenticación por tokens

En el caso de escenarios nativos y móviles donde algunos clientes no admiten cookies, la API de inicio de sesión proporciona un parámetro para solicitar tokens.

Advertencia

Se recomienda usar cookies para aplicaciones basadas en explorador, ya que el explorador controla cookies sin exponerlos a JavaScript. Si optas por usar la seguridad basada en tokens en aplicaciones web, eres responsable de garantizar que los tokens se mantengan seguros.

Se emite un token personalizado (uno que es propiedad de la plataforma ASP.NET Core Identity) que se puede usar para autenticar las solicitudes subsiguientes. El token se pasa en el encabezado Authorization como token de portador. También se proporciona un token de actualización. Este token permite a la aplicación solicitar un nuevo token cuando expira el antiguo sin forzar al usuario a iniciar sesión de nuevo.

Los tokens no son tokens web JSON (JWT) estándar. El uso de tokens personalizados es intencional, ya que la API integrada Identity está pensada principalmente para escenarios simples. La opción de tokens no está pensada para ser un proveedor de servicios de identity completo o un servidor de tokens, sino una alternativa a la opción de cookie para los clientes que no pueden usar cookies.

La siguiente guía comienza el proceso de implementación de la autenticación basada en tokens con la API de inicio de sesión. Se requiere código personalizado para completar la implementación. Para obtener más información, consulta Uso de Identity para proteger un back-end de API web para SPA.

En lugar de la API del servidor back-end que establece la autenticación cookie con una llamada a AddIdentityCookies en el generador de autenticación, la API de servidor configura la autenticación de token de portador con el método de extensión AddBearerToken. Especifica el esquema de los tokens de autenticación de portador con IdentityConstants.BearerScheme.

En Backend/Program.cs, cambia los servicios de autenticación y la configuración por lo siguiente:

builder.Services
    .AddAuthentication()
    .AddBearerToken(IdentityConstants.BearerScheme);

En BlazorWasmAuth/Identity/CookieAuthenticationStateProvider.cs, quita el parámetro de cadena de consulta useCookies en el método LoginAsync de CookieAuthenticationStateProvider:

- login?useCookies=true
+ login

En este momento, debes proporcionar código personalizado para analizar AccessTokenResponse en el cliente y administrar los tokens de acceso y actualización. Para obtener más información, consulta Uso de Identity para proteger un back-end de API web para SPA.

Otros escenarios de Identity

Escenarios cubiertos por el conjunto de Blazor documentación:

Para obtener información sobre escenarios adicionales Identity proporcionados por la API, consulte Uso Identity para proteger un back-end de API web para SPA:

  • Protección de los puntos de conexión seleccionados
  • Códigos de recuperación y autenticación en dos fases (2FA)
  • Administración de la información del usuario

Uso de flujos de autenticación seguros para mantener datos confidenciales y credenciales

Advertencia

No almacene secretos de aplicación, cadena de conexión s, credenciales, contraseñas, números de identificación personal (PIN), código C#/.NET privado o claves o tokens privados en el código del lado cliente, lo que siempre es inseguro. En entornos de prueba/ensayo y producción, el código del lado Blazor servidor y las API web deben usar flujos de autenticación seguros que eviten mantener las credenciales dentro del código del proyecto o los archivos de configuración. Fuera de las pruebas de desarrollo local, se recomienda evitar el uso de variables de entorno para almacenar datos confidenciales, ya que las variables de entorno no son el enfoque más seguro. Para las pruebas de desarrollo local, se recomienda la herramienta Secret Manager para proteger los datos confidenciales. Para obtener más información, consulte Mantener de forma segura los datos confidenciales y las credenciales.

Aplicaciones de muestra

En este artículo, las aplicaciones de ejemplo sirven como referencia para las aplicaciones independientes Blazor WebAssembly que acceden a ASP.NET Core Identity a través de una API web de back-end. La demostración incluye dos aplicaciones:

  • Backend: una aplicación de API web de back-end que mantiene un almacén de identity de usuario para ASP.NET Core Identity.
  • BlazorWasmAuth: una aplicación de front-end Blazor WebAssembly independiente con autenticación de usuario.

Accede a las aplicaciones de ejemplo a través de la carpeta de versión más reciente desde la raíz del repositorio con el vínculo siguiente. Los ejemplos se proporcionan para .NET 8 o posterior. Consulta el archivo README en la carpeta BlazorWebAssemblyStandaloneWithIdentity para ver los pasos sobre cómo ejecutar las aplicaciones de ejemplo.

Vea o descargue el código de ejemplo (cómo descargarlo)

Paquetes y código de la aplicación de API web de back-end

La aplicación de API web de back-end mantiene un almacén de identity de usuario para ASP.NET Core Identity.

Paquetes

La aplicación usa los siguientes paquetes NuGet:

Si la aplicación va a usar un proveedor de base de datos de EF Core diferente al proveedor en memoria, no cree una referencia de paquete en la aplicación para Microsoft.EntityFrameworkCore.InMemory.

En el archivo de proyecto de la aplicación (.csproj), se configura la globalización invariante.

Código de aplicación de ejemplo

La configuración de la aplicación configurar direcciones URL de back-end y front-end:

  • Aplicación de Backend (BackendUrl): https://localhost:7211
  • Aplicación deBlazorWasmAuth (FrontendUrl): https://localhost:7171

El Backend.http archivo se puede usar para probar la solicitud de datos meteorológicos. Ten en cuenta que la aplicación BlazorWasmAuth debe ejecutarse para probar el punto de conexión y el punto de conexión está codificado de forma dura en el archivo. Para obtener más información, consulta Usar archivos .http en Visual Studio 2022.

La siguiente instalación y configuración se encuentra en el Programarchivo de la aplicación.

identity de usuario con la autenticación de cookie se agrega mediante una llamada a AddAuthentication y AddIdentityCookies. Los servicios para comprobaciones de autorización se agregan mediante una llamada a AddAuthorizationBuilder.

Solo recomendada para demostraciones, la aplicación utiliza el EF Core proveedor de base de datos en memoria para el registro del contexto de la base de datos (AddDbContext). El proveedor de bases de datos en memoria facilita el reinicio de la aplicación y prueba los flujos de usuario de registro e inicio de sesión. Cada ejecución comienza con una base de datos nueva, pero la aplicación incluye código de demostración de la inicialización de un usuario de prueba, el cual se describe más adelante en este artículo. Si la base de datos se cambia a SQLite, los usuarios se guardan entre sesiones, pero la base de datos debe crearse mediante migraciones, como se muestra en el tutorial de introducción a EF Core†. Puedes usar otros proveedores relacionales como SQL Server para el código de producción.

Nota:

†El tutorial de introducción a EF Core usa comandos de PowerShell para ejecutar migraciones de base de datos al usar Visual Studio. Un enfoque alternativo en Visual Studio es usar la interfaz de usuario de servicios conectados:

En el Explorador de soluciones, haz doble clic en Servicios conectados. En Dependencias de servicio> SQL Server Express LocalDB, selecciona los puntos suspensivos (...) seguido de Agregar migración para crear una migración o Actualizar base de datos para actualizar la base de datos.

Configura Identity para usar la base de datos de EF Core y exponer los puntos de conexión de Identity a través de las llamadas a AddIdentityCore, AddEntityFrameworkStores y AddApiEndpoints.

Se establece una directiva de uso compartido de recursos entre orígenes (CORS) para permitir solicitudes de las aplicaciones front-end y back-end. Las direcciones URL de reserva están configuradas para la directiva CORS si la configuración de la aplicación no las proporciona:

  • Aplicación de Backend (BackendUrl): https://localhost:5001
  • Aplicación de BlazorWasmAuth (FrontendUrl): https://localhost:5002

Los servicios y los puntos de conexión para Swagger/OpenAPI se incluyen para la documentación de la API web y las pruebas de desarrollo. Para obtener más información sobre NSwag, consulta Introducción a NSwag y ASP.NET Core.

Las notificaciones de rol de usuario se envían desde una API mínima en el punto de conexión /roles.

Las rutas se asignan para los puntos de conexión de Identity mediante una llamada a MapIdentityApi<AppUser>().

Un punto de conexión de cierre de sesión (/Logout) está configurado en la canalización de middleware para cerrar la sesión de los usuarios.

Para proteger un punto de conexión, agrega el método de extensión RequireAuthorization a la definición de ruta. Para un controlador, agrega el atributo [Authorize] al controlador o a la acción.

Para obtener más información sobre los patrones básicos para la inicialización y configuración de una instancia DbContext, consulta Duración, configuración e inicialización de DbContext en la documentación EF Core.

Paquetes y código de aplicaciones de Blazor WebAssembly independientes de front-end

Una aplicación de front-end de Blazor WebAssembly independiente muestra la autenticación y autorización de usuario para acceder a una página web privada.

Paquetes

La aplicación usa los siguientes paquetes NuGet:

Código de aplicación de ejemplo

La carpeta Models contiene los modelos de la aplicación:

La interfaz IAccountManagement (Identity/CookieHandler.cs) proporciona servicios de administración de cuentas.

La clase CookieAuthenticationStateProvider (Identity/CookieAuthenticationStateProvider.cs) controla el estado de la autenticación basada en cookie y proporciona implementaciones del servicio de administración de cuentas descritas por la interfaz IAccountManagement. El método LoginAsync habilita explícitamente la autenticación cookie a través del valor de cadena de consulta useCookies de true. La clase también administra la creación de notificaciones de rol para usuarios autenticados.

La clase CookieHandler (Identity/CookieHandler.cs) garantiza cookie credenciales se envían con cada solicitud a la API web de back-end, que controla Identity y mantiene el almacén de datos Identity.

wwwroot/appsettings.file proporciona puntos de conexión de URL de back-end y front-end.

El App componente expone el estado de autenticación como parámetro en cascada. Para obtener más información, consulte Autenticación y autorización Blazor de ASP.NET Core.

El MainLayout componente y NavMenu componente usar el AuthorizeView componente para mostrar de forma selectiva el contenido en función del estado de autenticación del usuario.

Los siguientes componentes controlan las tareas comunes de autenticación de usuario, haciendo uso de servicios IAccountManagement :

El PrivatePage componente (Components/Pages/PrivatePage.razor) requiere autenticación y muestra las notificaciones del usuario.

Los servicios y la configuración se proporcionan en el Program archivo (Program.cs):

  • El controlador cookie se registra como un servicio con ámbito.
  • Los servicios de autorización están registrados.
  • El proveedor de estado de autenticación personalizado se registra como un servicio con ámbito.
  • La interfaz de administración de cuentas (IAccountManagement) está registrada.
  • La dirección URL del host base está configurada para una instancia de cliente HTTP registrada.
  • La dirección URL de back-end base está configurada para una instancia de cliente HTTP registrada que se usa para las interacciones de autenticación con la API web de back-end. El cliente HTTP usa el controlador cookie para asegurarse de que cookie credenciales se envían con cada solicitud.

Llame a AuthenticationStateProvider.NotifyAuthenticationStateChanged cuando cambie el estado de autenticación del usuario. Para obtener un ejemplo, vea los métodos LoginAsync y LogoutAsync de la clase CookieAuthenticationStateProvider (Identity/CookieAuthenticationStateProvider.cs).

Advertencia

El componente AuthorizeView selectivamente muestra el contenido de la interfaz de usuario dependiendo de si el usuario tiene la autorización necesaria. Todo el contenido de una aplicación Blazor WebAssembly colocada en un componente de AuthorizeView se puede detectar sin autenticación, por lo que el contenido confidencial debe obtenerse de una API web basada en servidor back-end después de que la autenticación se realice correctamente. Para obtener más información, consulte los siguientes recursos:

Demostración de la inicialización de un usuario de prueba

La clase SeedData (SeedData.cs) muestra cómo crear usuarios de prueba para el desarrollo. El usuario de prueba, llamado Leela, inicia sesión en la aplicación con la dirección de correo electrónico leela@contoso.com. La contraseña del usuario se establece en Passw0rd!. Se le asignan roles Administrator y Manager para la autorización, lo que permite al usuario acceder a la página del administrador en /private-manager-page, pero no a la página del editor en /private-editor-page.

Advertencia

Nunca permita que el código del usuario de prueba se ejecute en un entorno de producción. Solo se llama a SeedData.InitializeAsync en el entorno Development en el archivo Program:

if (builder.Environment.IsDevelopment())
{
    await using var scope = app.Services.CreateAsyncScope();
    await SeedData.InitializeAsync(scope.ServiceProvider);
}

Roles

Las notificaciones de rol no se envían desde el manage/info punto de conexión para crear notificaciones de usuario para los usuarios de la BlazorWasmAuth aplicación. Las notificaciones de rol se administran de forma independiente por medio de una solicitud independiente en el método GetAuthenticationStateAsync de la clase CookieAuthenticationStateProvider (Identity/CookieAuthenticationStateProvider.cs) después de que el usuario se autentique en el proyecto Backend.

En CookieAuthenticationStateProvider, se realiza una solicitud de roles al punto de conexión /roles del proyecto de API del servidor Backend. La respuesta se lee en una cadena llamando a ReadAsStringAsync(). JsonSerializer.Deserialize deserializa la cadena en una matriz RoleClaim personalizada. Por último, las notificaciones se agregan a la colección de notificaciones del usuario.

En la API del servidor Backend, en el archivo Program, una API mínima administra el punto de conexión /roles. Las notificaciones de RoleClaimType se seleccionanen un tipo anónimo y se serializan para devolverlas al proyecto BlazorWasmAuth con TypedResults.Json.

El punto de conexión /roles requiere autorización llamando a RequireAuthorization. Si decide no usar las API mínimas en favor de los controladores para los puntos de conexión de API de servidor seguro, asegúrese de establecer el atributo [Authorize] en controladores o acciones.

Hospedaje entre dominios (configuración del mismo sitio)

Las aplicaciones de ejemplo están configuradas para hospedar ambas aplicaciones en el mismo dominio. Si hospeda la aplicación de Backend en un dominio diferente al BlazorWasmAuth aplicación, quite la marca de comentario del código que configura el cookie (ConfigureApplicationCookie) en el archivoBackend de la aplicaciónProgram. Los valores predeterminados son:

Cambie los valores a:

- options.Cookie.SameSite = SameSiteMode.Lax;
- options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ options.Cookie.SameSite = SameSiteMode.None;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;

Para obtener más información sobre la configuración cookie del mismo sitio, consulta los siguientes recursos:

Compatibilidad con antiforgería

Solo el punto de conexión de cierre de sesión (/logout) de la aplicación Backend requiere atención para mitigar la amenaza de falsificación de solicitud entre sitios (CSRF).

El punto de conexión de cierre de sesión comprueba si hay un cuerpo vacío para evitar ataques CSRF. Al requerir un cuerpo, la solicitud debe realizarse desde JavaScript, que es la única manera de acceder a la autenticación cookie. No se puede acceder al punto de conexión de cierre de sesión mediante post basado en formularios. Esto impide que un sitio malintencionado inicie la sesión del usuario.

Además, el punto de conexión está protegido por autorización (RequireAuthorization) para evitar el acceso anónimo.

La aplicación cliente BlazorWasmAuth simplemente es necesaria para pasar un objeto vacío {} en el cuerpo de la solicitud.

Fuera del punto de conexión de cierre de sesión, la mitigación antiforgería solo es necesaria al enviar datos de formulario al servidor codificado como application/x-www-form-urlencoded, multipart/form-datao text/plain. Blazor administra la mitigación de CSRF para formularios en la mayoría de los casos. Para obtener más información, consulta Autenticación y autorizaciónBlazor ASP.NET Core e Información general sobre formularios BlazorASP.NET Core.

Las solicitudes a otros puntos de conexión de API de servidor (API web) con contenido codificado application/jsony CORS habilitado no requieren protección CSRF. Este es el motivo por el que no se requiere ninguna protección CSRF para elBackend punto de conexión de procesamiento de datos de la aplicación(/data-processing). Los roles (/roles) punto de conexión no necesitan protección CSRF porque es un punto de conexión GET que no modifica ningún estado.

Solución de problemas

Registro

Para habilitar el registro de depuración o seguimiento para la autenticación de Blazor WebAssembly, consulta el registro de Blazor en ASP.NET Core.

Errores comunes

Comprueba la configuración de cada proyecto. Confirma que las direcciones URL son correctas:

  • Proyecto Backend
    • appsettings.json
      • BackendUrl
      • FrontendUrl
    • Backend.http: Backend_HostAddress
  • Proyecto BlazorWasmAuth: wwwroot/appsettings.json
    • BackendUrl
    • FrontendUrl

Si la configuración parece correcta:

  • Analiza los registros de la aplicación.

  • Examina el tráfico de red entre la aplicación BlazorWasmAuth y la aplicación Backend con las herramientas de desarrollo del explorador. A menudo, después de realizar la solicitud, la aplicación de back-end devuelve al cliente un mensaje de error exacto o un mensaje con una pista sobre la causa del problema. En los siguientes artículos encontrarás instrucciones sobre las herramientas de desarrollo:

  • Google Chrome (documentación de Google)

  • Microsoft Edge

  • Mozilla Firefox (documentación de Mozilla)

El equipo de documentación responde a los comentarios y errores de los documentos en los artículos. Abre una incidencia con el vínculo Abrir una incidencia de documentación de la parte inferior del artículo. El equipo no puede proporcionar soporte técnico para el producto. Existen varios foros de soporte técnico públicos que ayudan a solucionar los problemas de una aplicación. Se recomienda lo siguiente:

Microsoft no posee ni controla ninguno de los foros anteriores.

Respecto a los informes de errores del marco que no son de seguridad ni confidenciales, o que no se pueden reproducir, abre una incidencia con la unidad de producto ASP.NET Core. No abras una incidencia con la unidad de producto hasta que hayas investigado exhaustivamente su causa y no puedas resolverlo por tu cuenta o con la ayuda de la comunidad en un foro de soporte técnico público. La unidad de producto no puede solucionar problemas de aplicaciones individuales cuyo funcionamiento se haya interrumpido debido a errores de configuración o casos de uso sencillos que involucren servicios de terceros. Si un informe es confidencial o delicado por naturaleza o describe un posible error de seguridad en el producto que los ciberdelincuentes puedan aprovechar, consulta Informes de problemas de seguridad y errores (repositorio de GitHub dotnet/aspnetcore).

Cookies y datos de sitios

Las cookies y los datos de sitios pueden persistir durante las actualizaciones de la aplicación e interferir con las pruebas y la solución de problemas. Al realizar cambios en el código de la aplicación, cambios en la cuenta de usuario o cambios en la configuración de la aplicación, borra lo siguiente:

  • Cookies de inicio de sesión de usuario
  • Cookies de aplicación
  • Datos de sitios almacenados y en caché

El enfoque siguiente sirve para evitar que las cookies persistentes y los datos de sitios interfieran con las pruebas y la solución de problemas:

  • Configuración de un explorador
    • Usa un explorador para las pruebas, y configúralo para que elimine todas las cookies y los datos del sitio cada vez que se cierre.
    • Asegúrate de que el explorador se cierra manualmente o mediante el IDE siempre que se produzca cualquier cambio en la aplicación, el usuario de prueba o la configuración del proveedor.
  • Usa un comando personalizado para abrir un explorador en el modo incógnito o privado en Visual Studio:
    • Abre el cuadro de diálogo Examinar con mediante el botón Ejecutar de Visual Studio.
    • Selecciona el botón Agregar.
    • Proporciona la ruta de acceso al explorador en el campo Programa. Las siguientes rutas de acceso del archivo ejecutable son ubicaciones de instalación típicas para Windows 10. Si el explorador está instalado en una ubicación diferente o no usa Windows 10, proporciona la ruta de acceso al archivo ejecutable del explorador.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • En el campo Argumentos, proporciona la opción de línea de comandos que utiliza el explorador para abrirse en el modo incógnito o privado. Algunos exploradores requieren la dirección URL de la aplicación.
      • Microsoft Edge: Usa -inprivate.
      • Google Chrome: usa --incognito --new-window {URL}, donde el marcador de posición {URL} es la dirección URL que se va a abrir (por ejemplo, https://localhost:5001).
      • Mozilla Firefox: usa -private -url {URL}, donde el marcador de posición {URL} es la dirección URL que se va a abrir (por ejemplo, https://localhost:5001).
    • Proporciona un nombre en el campo Nombre descriptivo. Por ejemplo: Firefox Auth Testing.
    • Selecciona el botón Aceptar.
    • Para evitar tener que seleccionar el perfil de explorador para cada iteración de pruebas con una aplicación, establece el perfil como predeterminado con el botón Establecer como predeterminado.
    • Asegúrate de que el explorador se cierra mediante el IDE siempre que se produzca cualquier cambio en la aplicación, el usuario de prueba o la configuración del proveedor.

Actualizaciones de aplicaciones

Una aplicación en funcionamiento deja de ejecutarse inmediatamente después de actualizar el SDK de .NET Core en la máquina de desarrollo o de cambiar las versiones del paquete en la aplicación. En algunos casos, los paquetes incoherentes pueden interrumpir una aplicación al realizar actualizaciones importantes. La mayoría de estos problemas puede corregirse siguiendo estas instrucciones:

  1. Borra las memorias caché del paquete NuGet del sistema local ejecutando dotnet nuget locals all --clear desde un shell de comandos.
  2. Elimina las carpetas bin y obj del proyecto.
  3. Restaura el proyecto y vuelve a compilarlo.
  4. Elimina todos los archivos de la carpeta de implementación del servidor antes de volver a implementar la aplicación.

Nota

No se pueden usar versiones de paquetes que no sean compatibles con la plataforma de destino de la aplicación. Para obtener información sobre un paquete, usa la galería de NuGet o el explorador de paquetes FuGet.

Inspección de las notificaciones del usuario

Para solucionar problemas con las notificaciones de usuario, el componente UserClaims siguiente se puede usar directamente en aplicaciones o servir como base para una mayor personalización.

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@attribute [Authorize]

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

**Name**: @AuthenticatedUser?.Identity?.Name

<h2>Claims</h2>

@foreach (var claim in AuthenticatedUser?.Claims ?? Array.Empty<Claim>())
{
    <p class="claim">@(claim.Type): @claim.Value</p>
}

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? AuthenticationState { get; set; }

    public ClaimsPrincipal? AuthenticatedUser { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthenticationState is not null)
        {
            var state = await AuthenticationState;
            AuthenticatedUser = state.User;
        }
    }
}

Recursos adicionales