高度な認証機能

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 を取得する

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 によるログインを試す

では、多要素認証の実装を試してみましょう。エミュレータが動作していることを確認し、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 を獲得しました。

オフィスから持ち上げダンスをしている人々の GIF