Habilitar la generación de código QR para aplicaciones de autenticación en una aplicación web TOTP en ASP.NET Core Blazor

En este artículo se explica cómo configurar una aplicación web ASP.NET Core Blazor con generación de código QR para aplicaciones de autenticación TOTP.

Para obtener una introducción a la autenticación en dos fases (2FA) con aplicaciones de autenticación mediante un algoritmo de contraseña de un solo uso (TOTP) basado en tiempo, consulte Habilitación de la generación de código QR para aplicaciones de autenticación TOTP en ASP.NET Core.

Aplicar scaffolding al componente de habilitar autenticador en la aplicación

Siga las instrucciones de scaffolding Identity en proyectos de ASP.NET Core para aplicar scaffolding Pages\Manage\EnableAuthenticator a la aplicación.

Nota:

Aunque solo se selecciona el componente EnableAuthenticator para scaffolding en este ejemplo, el scaffolding agrega actualmente todos los componentes de Identity a la aplicación. Además, se pueden producir excepciones durante el proceso de scaffolding en la aplicación. Si se producen excepciones al migrar una base de datos, detenga y reinicie la aplicación en cada excepción. Para obtener más información, consulte Excepciones de scaffolding para una aplicación web de Blazor (dotnet/Scaffolding #2694).

Tenga paciencia mientras se ejecutan las migraciones. En función de la velocidad del sistema, puede tardar hasta un minuto o dos en finalizar las migraciones de base de datos.

Para obtener más información, vea Scaffolding Identity en proyectos de ASP.NET Core. Para obtener instrucciones sobre cómo usar la CLI de .NET en lugar de Visual Studio, consulte el comando dotnet aspnet-codegenerator.

Agregar códigos QR a la página de configuración de 2FA

Estas instrucciones usan qrcode.js: un generador de QRCode entre exploradores para JavaScript (repositorio de GitHub davidshimjs/qrcodejs) de Shim Sangmin.

Descargue la biblioteca qrcode.min.js en la carpeta wwwroot del proyecto de servidor de la solución. La biblioteca no tiene dependencias.

En el componente App (Components/App.razor), coloque una referencia de script de biblioteca después de la etiqueta <script> de Blazor:

<script src="qrcode.min.js"></script>

El componente EnableAuthenticator, que forma parte del sistema de código QR de la aplicación y muestra el código QR a los usuarios, adopta la representación estática del lado servidor (SSR estático) con navegación mejorada. Por lo tanto, los scripts normales no se pueden ejecutar cuando el componente se carga o actualiza en la navegación mejorada. Se requieren pasos adicionales para desencadenar el código QR, con el fin de cargarlo en la interfaz de usuario cuando se carga la página. Para realizar la carga del código QR, se adopta el enfoque explicado en ASP.NET Core Blazor JavaScript con representación estática del lado servidor (SSR estático).

Agregue el siguiente inicializador de JavaScript a la carpeta wwwroot del proyecto de servidor. El marcador de posición {NAME} debe ser el nombre del ensamblado de la aplicación para que Blazor busque y cargue el archivo automáticamente. Si el nombre del ensamblado de la aplicación de servidor es BlazorSample, el archivo se denomina BlazorSample.lib.module.js.

wwwroot/{NAME}.lib.module.js:

const pageScriptInfoBySrc = new Map();

function registerPageScriptElement(src) {
  if (!src) {
    throw new Error('Must provide a non-empty value for the "src" attribute.');
  }

  let pageScriptInfo = pageScriptInfoBySrc.get(src);

  if (pageScriptInfo) {
    pageScriptInfo.referenceCount++;
  } else {
    pageScriptInfo = { referenceCount: 1, module: null };
    pageScriptInfoBySrc.set(src, pageScriptInfo);
    initializePageScriptModule(src, pageScriptInfo);
  }
}

function unregisterPageScriptElement(src) {
  if (!src) {
    return;
  }

  const pageScriptInfo = pageScriptInfoBySrc.get(src);
  
  if (!pageScriptInfo) {
    return;
  }

  pageScriptInfo.referenceCount--;
}

async function initializePageScriptModule(src, pageScriptInfo) {
  if (src.startsWith("./")) {
    src = new URL(src.substr(2), document.baseURI).toString();
  }

  const module = await import(src);

  if (pageScriptInfo.referenceCount <= 0) {
    return;
  }

  pageScriptInfo.module = module;
  module.onLoad?.();
  module.onUpdate?.();
}

function onEnhancedLoad() {
  for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
    if (referenceCount <= 0) {
      module?.onDispose?.();
      pageScriptInfoBySrc.delete(src);
    }
  }

  for (const { module } of pageScriptInfoBySrc.values()) {
    module?.onUpdate?.();
  }
}

export function afterWebStarted(blazor) {
  customElements.define('page-script', class extends HTMLElement {
    static observedAttributes = ['src'];

    attributeChangedCallback(name, oldValue, newValue) {
      if (name !== 'src') {
        return;
      }

      this.src = newValue;
      unregisterPageScriptElement(oldValue);
      registerPageScriptElement(newValue);
    }

    disconnectedCallback() {
      unregisterPageScriptElement(this.src);
    }
  });

  blazor.addEventListener('enhancedload', onEnhancedLoad);
}

Agregue el siguiente componente de PageScript compartido a la aplicación de servidor.

Components/PageScript.razor:

<page-script src="@Src"></page-script>

@code {
    [Parameter]
    [EditorRequired]
    public string Src { get; set; } = default!;
}

Agregue el siguiente archivo de JS colocado para el componente EnableAuthenticator, que se encuentra en Components/Account/Pages/Manage/EnableAuthenticator.razor. La función onLoad crea el código QR con la biblioteca qrcode.js de Sangmin mediante el URI de código QR generado por el método GenerateQrCodeUri en el bloque @code del componente.

Components/Account/Pages/Manage/EnableAuthenticator.razor.js:

export function onLoad() {
  const uri = document.getElementById('qrCodeData').getAttribute('data-url');
  new QRCode(document.getElementById('qrCode'), uri);
}

En el componente <PageTitle> del componente EnableAuthenticator, agregue el componente PageScript con la ruta de acceso al archivo JS colocado:

<PageScript Src="./Components/Account/Pages/Manage/EnableAuthenticator.razor.js" />

Nota:

Una alternativa al uso del enfoque con el componente PageScript consiste en usar un agente de escucha de eventos (blazor.addEventListener("enhancedload", {CALLBACK})) registrado en un inicializador de afterWebStartedJS para escuchar las actualizaciones de páginas causadas por una navegación mejorada. La devolución de llamada (marcador de posición {CALLBACK}) sigue la lógica de inicialización del código QR.

Con el enfoque de devolución de llamada con enhancedload, el código se ejecuta para cada navegación mejorada, incluso cuando el código QR <div> no se representa. Por lo tanto, se debe agregar código adicional para comprobar la presencia de <div> antes de ejecutar el código que agrega un código QR.

Elimine el elemento <div> que contiene las instrucciones de código QR:

- <div class="alert alert-info">
-     Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable 
-     QR code generation</a>.
- </div>

Busque los dos elementos <div> donde debe aparecer el código QR y donde se almacenan los datos del código QR en la página.

Haga los siguientes cambios:

  • Para el <div> vacío, asigne al elemento un id de qrCode.
  • Para el <div> con el atributo data-url, asigne al elemento un id de qrCodeData.
- <div></div>
- <div data-url="@authenticatorUri"></div>
+ <div id="qrCode"></div>
+ <div id="qrCodeData" data-url="@authenticatorUri"></div>

Cambie el nombre del sitio en el método GenerateQrCodeUri del componente EnableAuthenticator. El valor predeterminado es Microsoft.AspNetCore.Identity.UI. Cambie el valor a un nombre de sitio significativo que los usuarios puedan identificar fácilmente en su aplicación autenticadora junto con otros códigos QR para otras aplicaciones. Deje la dirección URL de valor codificada. Los desarrolladores suelen establecer un nombre de sitio que coincida con el nombre de la empresa. Examples: Yahoo, Amazon, Etsy, Microsoft, Zoho.

En el ejemplo siguiente, el nombre del sitio (empresa) irá en el marcador de posición {SITE NAME}:

private string GenerateQrCodeUri(string email, string unformattedKey)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        AuthenticatorUriFormat,
-       UrlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"),
+       UrlEncoder.Encode("{SITE NAME}"),
        UrlEncoder.Encode(email),
        unformattedKey);
}

Ejecute la aplicación y asegúrese de que el código QR es digitalizable y de que el código se valida.

El componente EnableAuthenticator en el origen de referencia

El componente EnableAuthenticator se puede inspeccionar en el origen de referencia:

El componente EnableAuthenticator de origen de referencia

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Recursos adicionales