ASP.NET Core Blazor Web App에서 TOTP 인증자 앱에 대한 QR 코드 생성 사용

이 문서에서는 TOTP 인증자 앱에 대한 QR 코드 생성을 사용하여 ASP.NET Core Blazor Web App을 구성하는 방법을 설명합니다.

TOTP(시간 기반 일회용 암호 알고리즘)를 사용하는 인증자 앱의 2FA(2단계 인증)에 대한 소개는 ASP.NET Core에서 TOTP 인증자 앱에 대한 QR 코드 생성 사용을 참조하세요.

앱에 Authenticator 사용 구성 요소 스캐폴드

ASP.NET Core 프로젝트의 스캐폴드의 Identity 지침에 따라 앱으로 스캐폴드 Pages\Manage\EnableAuthenticator 합니다.

참고 항목

이 예제에서는 EnableAuthenticator 스캐폴딩을 위해 구성 요소만 선택되었지만 스캐폴딩은 현재 모든 Identity 구성 요소를 앱에 추가합니다. 또한 앱으로 스캐폴딩하는 동안 예외가 throw될 수 있습니다. 데이터베이스 마이그레이션이 발생할 때 예외가 발생하면 앱을 중지하고 각 예외에서 앱을 다시 시작합니다. 자세한 내용은 웹앱에 대한 Blazor 스캐폴딩 예외(dotnet/Scaffolding#2694)를 참조하세요.

마이그레이션이 실행되는 동안 인내심을 가져야 합니다. 시스템 속도에 따라 데이터베이스 마이그레이션이 완료되는 데 최대 1~2분이 걸릴 수 있습니다.

자세한 내용은 ASP.NET Core 프로젝트에서 Identity 스캐폴드를 참조하세요. Visual Studio 대신 .NET CLI를 사용하는 방법에 대한 지침은 dotnet aspnet-codegenerator 명령을 참조하세요.

2FA 구성 페이지에 QR 코드 추가

이러한 지침은 Shim Sangmin의 qrcode.js: JavaScript용 브라우저 간 QRCode 생성기(davidshimjs/qrcodejsGitHub 리포지토리)를 사용합니다.

솔루션 서버 qrcode.min.js 프로젝트의 폴더에 wwwroot 라이브러리를 다운로드합니다. 라이브러리에는 종속성이 없습니다.

App 구성 요소(Components/App.razor)에서 라이브러리 스크립트 참조를 '의 <script> 태그 다음 위치에 Blazor배치합니다.

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

EnableAuthenticator 앱의 QR 코드 시스템의 일부이며 사용자에게 QR 코드를 표시하는 구성 요소는 향상된 탐색을 통해 정적 서버 쪽 렌더링(정적 SSR)을 채택합니다. 따라서 향상된 탐색에서 구성 요소가 로드되거나 업데이트될 때 일반 스크립트를 실행할 수 없습니다. 페이지가 로드될 때 UI에 로드할 QR 코드를 트리거하려면 추가 단계가 필요합니다. QR 코드 로드를 수행하기 위해 정적 서버 쪽 렌더링(정적 SSR)이 있는 ASP.NET Core Blazor JavaScript에 설명된 접근 방식이 채택되었습니다.

서버 프로젝트의 wwwroot 폴더에 다음 JavaScript 이니셜라이저를 추가합니다. {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!;
}

에 있는 Components/Account/Pages/Manage/EnableAuthenticator.razor구성 요소에 EnableAuthenticator 대해 다음 데이터 정렬 JS 된 파일을 추가합니다. 이 함수는 onLoad 구성 요소 @code 블록의 qrcode.js 메서드에서 생성된 GenerateQrCodeUri QR 코드 URI를 사용하여 Sangmin의 라이브러리를 사용하여 QR 코드를 만듭니다.

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 아래에 정렬된 JS 파일의 경로를 사용하여 구성 요소를 추가 PageScript 합니다.

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

참고 항목

구성 요소와 함께 PageScript 접근 방식을 사용하는 대안은 이니셜라이저afterWebStartedJS 등록된 이벤트 수신기(blazor.addEventListener("enhancedload", {CALLBACK}))를 사용하여 향상된 탐색으로 인한 페이지 업데이트를 수신 대기하는 것입니다. 콜백({CALLBACK} 자리 표시자)은 QR 코드 초기화 논리를 수행합니다.

콜백 방법을 enhancedload사용하면 QR 코드가 렌더링되지 않더라도 모든 향상된 탐색에 대해 코드 <div> 가 실행됩니다. 따라서 QR 코드를 추가하는 코드를 실행하기 전에 검사 <div> 추가 코드를 추가해야 합니다.

<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>

QR 코드가 표시되어야 하는 두 <div> 요소와 QR 코드 데이터가 페이지에 저장되는 위치를 찾습니다.

다음과 같이 변경합니다.

  • 비어 <div>있는 경우 요소에 다음을 idqrCode지정합니다.
  • with 특성의 <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입니다. 다른 앱의 다른 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)의 버전 태그를 선택하는 방법을 참조하세요.

추가 리소스