Habilitar a geração de código QR em aplicativos autenticadores TOTP em um aplicativo Web Blazor do ASP.NET Core

Este artigo explica como configurar um aplicativo Web Blazor do ASP.NET Core com a geração de código QR em aplicativos autenticadores TOTP.

Para ver uma introdução à 2FA (autenticação de dois fatores) com aplicativos autenticadores usando um algoritmo de TOTP (Senhas Avulsas por Tempo Limitado), confira Habilitar a geração de código QR em aplicativos autenticadores TOTP no ASP.NET Core.

Gerar o componente Habilitar Autenticador por scaffolding no aplicativo

Siga as diretrizes descritas em Gerar o Identity por scaffolding em projetos do ASP.NET Core para gerar Pages\Manage\EnableAuthenticator por scaffolding no aplicativo.

Observação

Embora apenas o componente EnableAuthenticator esteja selecionado para scaffolding neste exemplo, no momento, o scaffolding adiciona todos os componentes Identity ao aplicativo. Além disso, exceções podem ser geradas durante o processo de scaffolding no aplicativo. Se ocorrerem exceções quando ocorrerem migrações de banco de dados, interrompa o aplicativo e reinicie o aplicativo em cada exceção. Para obter mais informações, confira Exceções de scaffolding para um aplicativo Web Blazor (dotnet/Scaffolding nº 2694).

Tenha paciência enquanto as migrações são executadas. Dependendo da velocidade do sistema, as migrações de banco de dados podem demorar alguns minutos para serem concluídas.

Para obter mais informações, veja Scaffold Identity em projetos ASP.NET Core. Para obter diretrizes sobre como usar a CLI do .NET em vez do Visual Studio, confira Comando dotnet aspnet-codegenerator.

Adicionando códigos QR à página de configuração 2FA

Essas instruções usam o qrcode.js: gerador de QRCode entre navegadores para JavaScript de Shim Sangmin (repositório GitHub davidshimjs/qrcodejs).

Baixe a biblioteca qrcode.min.js para a pasta wwwroot do projeto de servidor da solução. A biblioteca não tem nenhuma dependência.

No componente App (Components/App.razor), coloque uma referência de script de biblioteca após a Blazormarca de <script>:

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

O componente EnableAuthenticator, que faz parte do sistema de código QR do aplicativo e exibe o código QR para os usuários, adota a SSR estática (renderização estática do lado do servidor) com a navegação aprimorada. Portanto, os scripts normais não podem ser executados quando o componente é carregado ou atualizado com a navegação aprimorada. São necessárias etapas extras para disparar o código QR a ser carregado na interface do usuário quando a página é carregada. Para fazer o carregamento do código QR, a abordagem explicada em JavaScript do Blazor no ASP.NET Core com a SSR estática (renderização estática do lado do servidor) é adotada.

Adicione o inicializador de JavaScript a seguir à pasta wwwroot do projeto do servidor. O espaço reservado {NAME} precisa ser o nome do assembly do aplicativo para que Blazor localize e carregue o arquivo automaticamente. Se o nome do assembly do aplicativo de servidor for BlazorSample, o arquivo será nomeado como 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);
}

Adicione o componente PageScript compartilhado a seguir ao aplicativo de servidor.

Components/PageScript.razor:

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

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

Adicione o arquivo JS colocado ao componente EnableAuthenticator, que está localizado em Components/Account/Pages/Manage/EnableAuthenticator.razor. A função onLoad cria o código QR com a biblioteca qrcode.js de Sangmin usando o URI de código QR produzido pelo método GenerateQrCodeUri no bloco @code do 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);
}

No componente <PageTitle> do componente EnableAuthenticator, adicione o componente PageScript com o caminho para o arquivo JS colocado:

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

Observação

Uma alternativa ao uso da abordagem com o componente PageScript é usar um ouvinte de eventos (blazor.addEventListener("enhancedload", {CALLBACK})) registrado em um inicializador afterWebStartedJSpara escutar as atualizações de página causadas pela navegação aprimorada. O retorno de chamada (espaço reservado {CALLBACK}) executa a lógica de inicialização do código QR.

Usando a abordagem de retorno de chamada com enhancedload, o código é executado para cada navegação aprimorada, mesmo quando o <div> do código QR não é renderizado. Portanto, um código adicional deve ser adicionado para verificar a presença do <div> antes da execução do código que adiciona um código QR.

Exclua o elemento <div> que contém as instruções do 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>

Localize os dois elementos <div> em que o código QR deve aparecer e o local em que os dados do código QR são armazenados na página.

Faça as seguintes alterações:

  • Para o <div> vazio, dê ao elemento uma id igual a qrCode.
  • Para o <div> com o atributo data-url, dê ao elemento uma id de qrCodeData.
- <div></div>
- <div data-url="@authenticatorUri"></div>
+ <div id="qrCode"></div>
+ <div id="qrCodeData" data-url="@authenticatorUri"></div>

Altere o nome do site no método GenerateQrCodeUri do componente EnableAuthenticator. O valor padrão é Microsoft.AspNetCore.Identity.UI. Altere o valor para um nome de site significativo que os usuários possam identificar com facilidade no aplicativo autenticador, juntamente com outros códigos QR para outros aplicativos. Mantenha o valor codificado por URL. Os desenvolvedores costumam definir um nome de site que corresponde ao nome da empresa. Exemplos: Yahoo, Amazon, Etsy, Microsoft, Zoho.

No exemplo a seguir, o espaço reservado {SITE NAME} é o local do nome do site (empresa):

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

Execute o aplicativo e verifique se o código QR é digitalizável e se o código é validado.

Componente EnableAuthenticator na fonte de referência

O componente EnableAuthenticator pode ser inspecionado na fonte de referência:

Componente EnableAuthenticator na fonte de referência

Observação

Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Recursos adicionais