Firebase Authenticationを使用したユーザー名認証の方法

本ドキュメントでは、Firebase Authenticationを利用してユーザー名での認証を実装する方法を紹介します。Firebase Authenticationはメールアドレスを使用した認証を直接サポートしていますが、ユーザー名での直接的な認証はサポートしていません。ここでは、ユーザー名をメールアドレス形式で利用するアプローチを取り、具体的にはサンプルアプリでは、電話番号をユーザー名として使用するケースを例に説明します。

また、今回のサンプルは、下記の運用を前提とします。

  • NCMB会員管理システムにて電話番号が保持されていること。

  • ユーザー名として電話番号を使用すること。

  • データ移行時には初期パスワードを設定すること。

  • ユーザーがログイン後にパスワードを変更可能であること。

本手法の特徴は、メールアドレスの検証を行わないことです。

このアプローチの欠点は、メールアドレスをダミーで使用するため、ユーザーがパスワードを忘れた場合にパスワード再設定機能を利用できないことにあります。そのため、パスワードの再設定は管理者への依頼を想定した運用が必要です。

本手法では、ダミーメールアドレスの使用により、通常のパスワード再設定メカニズムを利用できません。パスワード忘れの場合は管理者への連絡が必要となります。

本ドキュメントで扱うコードのオリジナルプロジェクトは、GitHubリポジトリにて公開しています。

データ移行

会員管理 データの移行」で解説したスクリプトを利用します。Firebaseのセットアップなどの詳細はそちらを参照ください。 今回は、「importWithUserName.js」を利用します。

インポート実行

インポートは、次のコマンドで実行します。登録されたデータの確認などは前回の記事を参照してください。

npm run import-name

importUser関数の解説

importWithUserName.js では、NCMB の会員管理機能に登録されたユーザー情報と、Firebase Authenticationのユーザー情報を紐づける処理を行います。

ユーザー名のFirebase対応形式への変換

NCMBで管理されているユーザー名(ここでは電話番号を例としています)をFirebase Authenticationで利用可能な形式に変換するために、次の手順を踏みます。

  • ユーザー名(電話番号)に対して、ダミードメイン(@example.com)を追加します。

  • この変換により、ユーザー名がメールアドレス形式になります。

変換の必要性

Firebase Authenticationでは、ユーザーを識別するためにメールアドレス形式のデータが必要です。そのため、元々メールアドレス形式でないユーザー名(この場合は電話番号)をFirebaseで扱うためには、メールアドレス形式に変換する必要があります。この変換を行わないと、Firebase側でエラーが発生し、ユーザー情報の登録や認証が正しく行えません。

const phoneNumber = formatPhoneNumber(user.phone_number);
const dummyEmail = `${phoneNumber}@example.com`;

try {
    const results = await getAuth()
        .importUsers(
            [
                {
                    uid: user.objectId,                                 // NCMBのユーザーIDをFirebaseのUIDにマッピングします。
                    displayName: formatPhoneNumber(user.phone_number),  // NCMBの電話番号を表示名(displayName)にマッピングします。
                    email: dummyEmail,                                  // NCMBの電話番号をFirebaseのメールアドレスにマッピングします。
                    emailVerified: true,                                // メールアドレスの確認状態。メールアドレスの検証を行わないため、初期状態を確認済(true)としています。
                },
            ]
        );

今回のサンプルではメールアドレスの検証を行わないので emailVerifiedtrue (検証済み)に設定します。

初期パスワードの登録

移行時に初期パスワードを登録します。 まずメールアドレスを使って、Authenticationから該当データを取得します。

const userRecord = await admin.auth().getUserByEmail(dummyEmail);
console.log("UID:", userRecord.uid);

次に、取得したユーザーデータのuidupdateUser 関数を使ってパスワードを変更します。 このサンプルでは test123 という固定値を使っていますが、ユーザーごとに十分な長さを持つランダム文字列を使うようにしてください。

await getAuth().updateUser(userRecord.uid, {
    password: 'test123',
});

この updateUser 関数は初期パスワードの登録以外にも、ユーザー管理者がパスワード変更をするケースなどにも利用できます。

ユーザー名を使った認証サンプルアプリ

本項では、移行したユーザー名と初期パスワードを使ってログインを行うサンプルアプリを解説します。前回紹介したサンプルに、下記機能を追加しました。

  • ユーザー名(電話番号)、パスワードでアプリにログインする

  • ログイン状態で、パスワード変更を行う

ユーザー名、パスワードでアプリにログインする

下記のコードは「ユーザー名でログイン」ボタンがタップされた時に実行されます。 AuthenticationのsignInWithEmailAndPassword 関数を使いますが、画面から入力されたユーザー名(電話番号)をメールアドレス形式に加工して利用します。 「@example.com」部分は移行スクリプトで指定したドメインと同じものを使います。 パスワードには初期パスワード「test123」を入力します。

const onClickLoginWithUserName = () => {
    const userName = document.getElementById("userName").value;
    const password = document.getElementById("passwordForUserName").value;

    // emailは画面上では利用しないが、ログインで利用するために userName + 任意のドメイン形式で保存する。
    // 「example.com」部分はアプリケーションに応じて変更する。ユーザーデータの移行は https://github.com/monaca-samples/authentication-migration の importWithUserName.jsを参照。
    const email = `${userName}@example.com`;
    signInWithEmailAndPassword(auth, email, password)
        .then((userCredential) => {
            // ログイン成功
            alert("ログインしました");
            const user = userCredential.user;
            console.log(user);
        })
        .catch((error) => {
            alert("ログインエラーです");
            console.error(error);
        });
};

パスワード変更

初期パスワードを使い続けるのはセキュリティ上よくないので、ログインしたユーザー自身で変更する機能が必要です。 前項で紹介した「パスワード再設定」機能はパスワードを忘れてしまってログインできないケースを想定していますが、パスワード変更機能はログイン後にパスワードを変更するケースを想定しています。 下記のコードはログイン後に表示される「パスワード変更」ボタンがタップされた時に実行されます。 AuthenticationのupdatePassword 関数にログインユーザー、新しいパスワードを指定して実行します。

const onClickPasswordChange = () => {
    const newPassword = document.getElementById("newPassword").value;
    updatePassword(auth.currentUser, newPassword).then(() => {
        alert("パスワードを変更しました");
    }).catch((error) => {
        console.error(error);
    });
};

最終更新