🤖

[Firebase Authentication]バックエンドでpasswordユーザーにプロバイダをリンクする方法

2021/09/06に公開
1

はじめに

通常はFirebaseで業務をしています。そのときに、実装した内容を今回は記載していきます。

背景

Firebase Authenticationでpasswordプロバイダのユーザーを管理している。
このときに、他のプロバイダ(googleやmicrosoft.comなど)をバックエンドで行いたい。
公式は、クライアント側で実装する内容をサンプルとしてあげているが、バックエンドのサンプルは存在していない。
今回はなぜ、クライアント側で実装しなかったのか、バックエンドの実装方法を残しておく。
※細かい設定は省いていますが、設定で「同じメールを使用するアカウントをリンクする」が必要なので、事前に設定をしておいてください。

クライアントの実装方法

以下は公式のサンプルです

auth.signInWithPopup(new firebase.auth.OAuthProvider('microsoft.com')).catch(function(error) {
  if (error.code === 'auth/account-exists-with-different-credential') {
    var pendingCred = error.credential;
    var email = error.email;
      if (methods[0] === 'password') {
        var password = promptUserForPassword(); 
        auth.signInWithEmailAndPassword(email, password).then(function(result) {
          return result.user.linkWithCredential(pendingCred);
        }).then(function() {
          goToApp();
        });
        return;
      }
        result.user.linkAndRetrieveDataWithCredential(pendingCred).then(function(usercred) {

          goToApp();
        });
      });
    });
  }
});

https://firebase.google.com/docs/auth/web/microsoft-oauth?hl=ja#handling-account-exists-with-different-credential-errors
データの流れは以下です。

  1. プロバイダ認証のポップアップ画面 or リダイレクト画面を表示させる
  2. ログイン実施
  3. 「account-exists-with-different-credential」エラー発生する
  4. エラー発生ときに含まれる返り値のemailとクレデンシャル情報を定数に入れておく
  5. fetchSignInMethodsForEmail()でemailプロバイダ情報を取得
  6. 非同期でパスワード取得
  7. 5と6で取得して情報を元に、emailとpassword認証を行う
  8. 7の返り値のresult.user.linkWithCredential()に4で取得したクレデンシャル情報を引数を用いて、プロバイダにリンクを行う
  9. プロバイダリンク完了

ここで問題だったのは、6. 非同期でパスワード取得です。仕様上パスワードをFirebase Authenticationのパスワードで管理を行なっていたので、パスワードを取得することができませんでした。
画面を遷移して、パスワードを入力させる画面を出す方法も考えたのですが、UXを考慮してやめました。
なので、バックエンド側で簡潔する方法を考えました。

実装方法(バックエンド)

業務ではmicrosoftプロバイダをリンクさせる必要があったので、サンプルはmicrosoftで記載していきます。
※一部GraphAPIの情報が必要なってきます。

ざっくりな流れは以下です。

  1. Firebase Authenticationで管理しているUIDを取得する
  2. 事前にGraphAPIでオブジェクトIDを取得する
  3. updateUser()でプロバイダのリンクを行う
 return new Promise(async (resolve, reject) => {
      try {
        await admin.auth().updateUser(uid, {
          providerToLink: {
            uid: //microsoftのオブジェクトID,
            displayName: //任意の名前,
            email: //任意のemail,
            phoneNumber: null,
            photoURL: null,
            providerId: "microsoft.com", // 紐づけたいプロバイダ
          },
        });
        resolve({ code: 200 });
      } catch (e) {
        reject({ code: 500 });
      }
    });

ポイントはpriderToLinkです。ここで、uidをきちんとmicrosoftオブジェクトIDを紐づける必要があります。もし、別のidを紐づけてしまうと、正常にプロバイダログインができなくなくなります。
uidに紐づける値は、プロバイダで変わってくると思われます。事前にどんな値が入るのか、調査は必要になってきます。
この後はクライアント側で通常どおり、プロバイダ認証をすれば正常にログインができるかと思います。

https://firebase.google.com/docs/reference/admin/node/admin.auth.UpdateRequest?hl=ja
https://firebase.google.com/docs/reference/admin/node/admin.auth.UserProvider?hl=ja

さいごに

今回はバックエンドでFirebase Authenticationを実装してみました。みにくいですが、公式のドキュメントはかなり参考になりました。公式のドキュメントを参考にすることで、バックエンドの実装もかなり幅広くなるので、公式のサンプルで解決できない時は、参考にしていこうと思っています。

参考記事

https://firebase.google.com/docs/auth/web/microsoft-oauth?hl=ja#handling-account-exists-with-different-credential-errors
https://firebase.google.com/docs/reference/admin/node/admin.auth.UpdateRequest?hl=ja
https://firebase.google.com/docs/reference/admin/node/admin.auth.UserProvider?hl=ja

Discussion