Включение создания QR-кода для приложений проверки подлинности TOTP в ASP.NET Core Blazor Web App

В этой статье объясняется, как настроить ASP.NET Core Blazor Web App с помощью СОЗДАНИЯ QR-кода для приложений проверки подлинности TOTP.

Общие сведения о двухфакторной проверке подлинности (2FA) с приложениями authenticator с помощью однократного алгоритма паролей на основе времени (TOTP) см. в статье "Включение создания QR-кода для приложений проверки подлинности TOTP" в ASP.NET Core.

Создание шаблона компонента Enable Authenticator в приложение

Следуйте инструкциям в разделе "Шаблон" Identity в ASP.NET Основных проектов , чтобы создать Pages\Manage\EnableAuthenticator шаблон в приложении.

Примечание.

Хотя только EnableAuthenticator компонент выбран для формирования шаблонов в этом примере, создание шаблонов в настоящее время добавляет все Identity компоненты в приложение. Кроме того, во время процесса формирования шаблонов в приложение могут возникать исключения. Если возникают исключения при миграции баз данных, остановите приложение и перезапустите приложение по каждому исключению. Дополнительные сведения см. в разделе "Создание шаблонов исключений для Blazor веб-приложения" (dotnet/Scaffolding #2694).

Будьте терпеливыми во время выполнения миграции. В зависимости от скорости системы может потребоваться до двух минут для завершения миграции баз данных.

Дополнительные сведения см. в разделе Шаблоны Identity в проектах ASP.NET Core. Инструкции по использованию .NET CLI вместо Visual Studio см . в команде dotnet aspnet-codegenerator.

Добавление QR-кодов на страницу конфигурации 2FA

Эти инструкции используют qrcode.js Shim Sangmin: генератор QRCode для JavaScriptdavidshimjs/qrcodejs (репозиторий GitHub).

Скачайте библиотеку qrcode.min.js в wwwroot папку проекта сервера решения. Библиотека не имеет зависимостей.

В компоненте App (Components/App.razor) поместите ссылку на скрипт библиотеки после Blazorтега<script>:

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

Компонент EnableAuthenticator , который входит в систему QR-кода в приложении и отображает QR-код пользователям, принимает статическую отрисовку на стороне сервера (статический SSR) с улучшенной навигацией. Поэтому обычные скрипты не могут выполняться при загрузке или обновлении компонента при расширенной навигации. Дополнительные шаги необходимы для активации QR-кода для загрузки в пользовательском интерфейсе при загрузке страницы. Для загрузки QR-кода используется подход, описанный в ASP.NET Core Blazor JavaScript со статическим отображением на стороне сервера (статический SSR).

Добавьте следующий инициализатор JavaScript в папку проекта wwwroot сервера. Заполнитель {NAME} должен быть именем сборки Blazor приложения для автоматического поиска и загрузки файла. Если имя сборки серверного приложения имеет имя BlazorSample, файл называется 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);
}

Добавьте следующий общий PageScript компонент в серверные приложения.

Components/PageScript.razor:

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

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

Добавьте следующий файл с сортировкой EnableAuthenticatorJS для компонента, расположенного по адресуComponents/Account/Pages/Manage/EnableAuthenticator.razor. Функция onLoad создает QR-код с библиотекой Sangmin qrcode.js с помощью URI QR-кода, созданного GenerateQrCodeUri методом в блоке компонента @code .

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

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

<PageTitle> В разделе компонента в EnableAuthenticator компоненте добавьте PageScript компонент с путем к файлу с сортировкойJS:

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

Примечание.

Альтернативой использованию подхода с PageScript компонентом является использование прослушивателя событий (blazor.addEventListener("enhancedload", {CALLBACK})) зарегистрированного в инициализатореafterWebStartedJS для прослушивания обновлений страниц, вызванных расширенной навигацией. Обратный вызов ({CALLBACK} заполнитель) выполняет логику инициализации QR-кода.

Использование подхода обратного вызова с enhancedloadкодом выполняется для каждой расширенной навигации, даже если QR-код <div> не отображается. Поэтому необходимо добавить дополнительный код в проверка для наличия <div> кода перед выполнением кода, добавляющего QR-код.

<div> Удалите элемент, содержащий инструкции QR-кода:

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

Найдите два <div> элемента, где должен отображаться QR-код и где хранятся данные QR-кода на странице.

Внесите следующие изменения:

  • Для пустого <div>, присвойте элементу idqrCodeзначение .
  • <div> Для атрибута data-url присвойте элементу idqrCodeData.
- <div></div>
- <div data-url="@authenticatorUri"></div>
+ <div id="qrCode"></div>
+ <div id="qrCodeData" data-url="@authenticatorUri"></div>

Измените имя сайта в GenerateQrCodeUri методе EnableAuthenticator компонента. Значение по умолчанию — Microsoft.AspNetCore.Identity.UI. Измените значение на понятное имя сайта, которое пользователи могут легко определить в приложении authenticator вместе с другими QR-кодами для других приложений. Оставьте закодированный URL-адрес значения. Разработчики обычно задают имя сайта, соответствующее имени компании. Примеры: Yahoo, Amazon, Etsy, Microsoft, Zoho.

В следующем примере {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);
}

Запустите приложение и убедитесь, что QR-код можно проверить и проверить код.

EnableAuthenticator компонент в эталонном источнике

Компонент EnableAuthenticator можно проверить в справочном источнике:

EnableAuthenticator компонент в эталонном источнике

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Дополнительные ресурсы