고급 인증 기능

1. 설정

소스 코드 가져오기

이 Codelab에서는 거의 완성된 친근한 채팅 샘플 앱 버전으로 시작하므로 먼저 소스 코드를 클론해야 합니다.

$ git clone https://github.com/firebase/codelab-friendlychat-web --branch security

그런 다음 security-start 디렉터리로 이동하여 이 Codelab의 나머지 부분을 진행합니다.

$ cd codelab-friendlychat-web/security-start

이제 코드를 실행할 수 있도록 종속 항목을 설치합니다. 인터넷 연결이 느린 경우 1~2분 정도 걸릴 수 있습니다.

$ npm install && (cd functions && npm install)

이 저장소 알아보기

security-solution/ 디렉터리에는 샘플 앱의 전체 코드가 포함되어 있습니다. security-start 디렉터리는 이 Codelab을 진행하게 되며 인증 구현의 몇 가지 중요한 부분이 누락되어 있습니다. security-start/security-solution/의 주요 파일과 기능은 다음과 같습니다.

  • functions/index.js에는 Cloud Functions 코드가 포함되며, 여기에서 인증 차단 함수를 작성합니다.
  • public/ - 채팅 앱의 정적 파일이 포함되어 있습니다.
  • public/scripts/main.js - 채팅 앱 JS 코드 (src/index.js)가 컴파일되는 위치
  • src/firebase-config.js - 채팅 앱을 초기화하는 데 사용되는 Firebase 구성 객체를 포함합니다.
  • src/index.js - 채팅 앱 JS 코드

Firebase CLI 가져오기

에뮬레이터 도구 모음은 Firebase CLI (명령줄 인터페이스)의 일부이며 다음 명령어로 머신에 설치할 수 있습니다.

$ npm install -g firebase-tools@latest

webpack으로 자바스크립트를 빌드하면 public/scripts/ 디렉터리 내에 main.js가 생성됩니다.

webpack build

다음으로 최신 버전의 CLI가 있는지 확인합니다. 이 Codelab은 버전 11.14 이상에서 작동합니다.

$ firebase --version
11.14.2

Firebase 프로젝트에 연결

Firebase 프로젝트가 없으면 Firebase Console에서 새 Firebase 프로젝트를 만듭니다. 나중에 필요하므로 선택한 프로젝트 ID를 기록해 둡니다.

이제 이 코드를 Firebase 프로젝트에 연결해야 합니다. 먼저 다음 명령어를 실행하여 Firebase CLI에 로그인합니다.

$ firebase login

다음으로, 다음 명령어를 실행하여 프로젝트 별칭을 만듭니다. $YOUR_PROJECT_ID를 Firebase 프로젝트의 ID로 바꿉니다.

$ firebase use $YOUR_PROJECT_ID

이제 앱을 실행할 준비가 되었습니다.

2. 에뮬레이터 실행

이 섹션에서는 앱을 로컬에서 실행합니다. 즉, 에뮬레이터 도구 모음을 부팅할 차례입니다.

에뮬레이터 시작

Codelab 소스 디렉터리 내에서 다음 명령어를 실행하여 에뮬레이터를 시작합니다.

$ firebase emulators:start

이렇게 하면 http://127.0.0.1:5170에서 앱을 제공하고 변경사항이 있을 때마다 소스 코드를 지속적으로 다시 빌드합니다. 변경사항을 보려면 브라우저에서 로컬로만 강제 새로고침 (ctrl-shift-r)하면 됩니다.

다음과 같은 출력이 표시됩니다.

i  emulators: Starting emulators: auth, functions, firestore, hosting, storage
✔  functions: Using node@16 from host.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  hosting[demo-example]: Serving hosting files from: ./public
✔  hosting[demo-example]: Local server: http://127.0.0.1:5170
i  ui: Emulator UI logging to ui-debug.log
i  functions: Watching "[...]" for Cloud Functions...
✔  functions: Loaded functions definitions from source: beforecreated.
✔  functions[us-central1-beforecreated]: providers/cloud.auth/eventTypes/user.beforeCreate function initialized (http://127.0.0.1:5011/[...]/us-central1/beforecreated).
i  Running script: npm start
 
> security@1.0.0 start
> webpack --watch --progress
[...]
webpack 5.50.0 compiled with 1 warning in 990 ms

모든 에뮬레이터가 준비됨 메시지가 표시되면 앱을 사용할 수 있습니다.

3. MFA 구현

이 저장소에 MFA가 부분적으로 구현되어 있습니다. 코드를 추가하여 먼저 사용자를 MFA에 등록한 후 MFA에 등록된 사용자에게 두 번째 단계를 표시하라는 메시지를 표시합니다.

편집기에서 src/index.js 파일을 열고 startEnrollMultiFactor() 메서드를 찾습니다. 다음 코드를 추가하여 전화 악용을 방지하는 reCAPTCHA 인증기를 설정합니다 (reCAPTCHA 인증기는 보이지 않도록 설정되어 사용자에게 표시되지 않음).

async function startEnrollMultiFactor(phoneNumber) {
  const recaptchaVerifier = new RecaptchaVerifier(
    "recaptcha",
    { size: "invisible" },
    getAuth()
  );

그런 다음 finishEnrollMultiFactor() 메서드를 찾고 다음을 추가하여 두 번째 단계를 등록합니다.

// Completes MFA enrollment once a verification code is obtained.
async function finishEnrollMultiFactor(verificationCode) {
  // Ask user for the verification code. Then:
  const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
  const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
 
  // Complete enrollment.
  await multiFactor(getAuth().currentUser)
    .enroll(multiFactorAssertion)
    .catch(function (error) {
      alert(`Error finishing second factor enrollment. ${error}`);
      throw error;
    });
  verificationId = null;
}

다음으로, signIn 함수를 찾고 MFA에 등록한 사용자에게 2단계 인증을 입력하라는 메시지를 표시하는 다음 제어 흐름을 추가합니다.

async function signIn() {
  // Sign in Firebase using popup auth and Google as the identity provider.
  var provider = new GoogleAuthProvider();
  await signInWithPopup(getAuth(), provider)
    .then(function (userCredential) {
      // User successfully signed in and is not enrolled with a second factor.
    })
    .catch(function (error) {
      if (error.code == "auth/multi-factor-auth-required") {
        multiFactorResolver = getMultiFactorResolver(getAuth(), error);
        displaySecondFactor(multiFactorResolver.hints);
      } else {
        alert(`Error signing in user. ${error}`);
      }
    });
}

여기서 호출된 함수를 포함하여 나머지 구현은 이미 완료되었습니다. 어떻게 작동하는지 확인하려면 파일의 나머지 부분을 살펴보세요.

4. 에뮬레이터에서 MFA로 로그인 사용해 보기

이제 MFA 구현을 시도해 보겠습니다. 에뮬레이터가 여전히 실행 중인지 확인하고 로컬 호스팅 앱(localhost:5170)을 방문하세요. 로그인해 보세요. MFA 코드를 입력하라는 메시지가 표시되면 터미널 창에 MFA 코드가 표시됩니다.

에뮬레이터는 다중 인증(MFA)을 완벽하게 지원하므로 개발 환경이 완전히 독립적일 수 있습니다.

MFA 구현에 대한 자세한 내용은 참조 문서를 확인하세요.

5. 차단 함수 만들기

일부 애플리케이션은 특정 사용자 그룹만 사용하도록 설계되었습니다. 이러한 경우에 사용자가 앱에 가입하거나 로그인하는 데 필요한 맞춤 요구사항을 만들 수 있어야 합니다.

차단 함수는 커스텀 인증 요구사항을 생성하는 방법을 제공합니다. Cloud Functions이기는 하지만 대부분의 함수와 달리 사용자가 가입이나 로그인을 시도할 때 동기식으로 실행됩니다.

차단 함수를 만들려면 편집기에서 functions/index.js을 열고 주석 처리된 beforecreated 함수를 찾습니다.

example.com 도메인을 가진 사용자만 계정을 만들 수 있는 다음 코드로 교체합니다.

exports.beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  // Only users of a specific domain can sign up.
  if (!user.email || !user.email.endsWith("@example.com")) {
    throw new HttpsError("invalid-argument", "Unauthorized email");
  }
});

6. 에뮬레이터에서 차단 함수 사용해 보기

차단 함수를 사용해 보려면 에뮬레이터가 실행 중인지 확인하고 웹 앱(localhost:5170)에서 로그아웃하세요.

그런 다음 example.com(으)로 끝나지 않는 이메일 주소로 계정을 만들어 보세요. 차단 함수는 작업이 성공하지 못하게 합니다.

이제 example.com(으)로 끝나는 이메일 주소로 다시 시도해 주세요. 계정이 생성됩니다.

차단 함수를 사용하면 인증에 필요한 제한사항을 만들 수 있습니다. 자세한 내용은 참조 문서를 확인하세요.

요약

훌륭합니다. 사용자가 계정을 안전하게 보호할 수 있도록 웹 앱에 다중 인증(MFA)을 추가한 다음 차단 함수를 사용하여 사용자가 가입하는 데 필요한 커스텀 요구사항을 만들었습니다. GIF를 획득하셨습니다.

사무실에서 지붕 춤을 추는 사람들의 GIF