高度な認証機能

1. セットアップ

ソースコードを取得する

この Codelab では、ほぼ完成したバージョンの Friendly Chat サンプルアプリから始めます。まず、ソースコードをクローン化する必要があります。

$ 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/ - Chat アプリの静的ファイルが格納されます
  • public/scripts/main.js - チャットアプリの JS コード(src/index.js)のコンパイル先
  • src/firebase-config.js - チャットアプリの初期化に使用される Firebase 構成オブジェクトが含まれます。
  • src/index.js - Chat アプリの JS コード

Firebase CLI を入手する

Emulator Suite は Firebase CLI(コマンドライン インターフェース)の一部です。次のコマンドを使用してマシンにインストールできます。

$ npm install -g firebase-tools@latest

webpack を使用して JavaScript を作成します。これにより、public/scripts/ ディレクトリ内に main.js が作成されます。

webpack build

次に、CLI が最新バージョンであることを確認します。この Codelab はバージョン 11.14 以降で動作します。

$ firebase --version
11.14.2

Firebase プロジェクトに接続する

Firebase プロジェクトがない場合は、Firebase コンソールで新しい Firebase プロジェクトを作成します。後で必要になるため、選択したプロジェクト ID をメモしておきます。

次に、このコードを Firebase プロジェクトに接続する必要があります。まず、次のコマンドを実行して Firebase CLI にログインします。

$ firebase login

次に、次のコマンドを実行してプロジェクト エイリアスを作成します。$YOUR_PROJECT_ID は、Firebase プロジェクトの ID に置き換えます。

$ firebase use $YOUR_PROJECT_ID

これでアプリを実行する準備が整いました。

2. エミュレータを実行する

このセクションでは、アプリをローカルで実行します。これで、Emulator Suite を起動できます。

エミュレータを起動する

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

[All emulators ready] メッセージが表示されたら、アプリを使用できる状態です。

3. MFA の実装

このリポジトリでは、MFA が部分的に実装されています。まずユーザーを多要素認証に登録し、次に多要素認証に登録したユーザーに第 2 の要素を要求するプロンプトを表示するコードを追加します。

エディタで src/index.js ファイルを開き、startEnrollMultiFactor() メソッドを見つけます。次のコードを追加して、電話の不正使用を防止する reCAPTCHA ベリファイアを設定します(reCAPTCHA ベリファイアは非表示に設定されているため、ユーザーには表示されません)。

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

次に、finishEnrollMultiFactor() メソッドを見つけて以下を追加し、第 2 要素を登録します。

// 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 関数を見つけて、多要素認証に登録したユーザーに第 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 の実装の詳細については、リファレンス ドキュメントをご覧ください。

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 のメールアドレスでもう一度お試しください。アカウントは正常に作成されます。

ブロッキング関数を使用すると、認証に必要な制限を作成できます。詳細については、リファレンス ドキュメントをご覧ください。

内容のまとめ

お疲れさまでした。ユーザーがアカウントを安全に保つために、ウェブアプリに多要素認証を追加しました。次に、ブロック機能を使用してユーザーが登録するためのカスタム要件を作成しました。GIF を獲得しました。

オフィスで「Raise the Roof」ダンスを踊る人々の GIF