🌛

【Firebase】ユーザーアカウントの削除方法

2023/01/28に公開

Firebaseでのユーザーアカウントの削除方法

Firebase

はじめに

筆者、初めてのFirebaseを使って認証機能を作成してました。
しかし、Authenticationからユーザーのアカウントを削除する方法が公式でも不完全であったためここに記しておきたいと思います。

同じく「どうやってやるの?!」と彷徨っている人の助けになれば幸いです🦧

前提

  • 認証方法:GoogleAuthProviderのポップアップモード
  • Cloud Fonctionsは使わない(お金かかるから)
     →Cloud Functions使えば再認証自体不要でただdeleteUser実行すればいけるっぽい?

つまづきポイント

🌵単純にdeleteUser(user)を実行するだけではできない

↓これにも書いてあるように、deleteUser(user)を実行することでユーザー削除自体はできるはずだけど...

https://firebase.google.com/docs/auth/web/manage-users?hl=ja#delete_a_user

import { getAuth, deleteUser } from "firebase/auth";

const auth = getAuth();
const user = auth.currentUser;

deleteUser(user).then(() => {
  // User deleted.
}).catch((error) => {
  // An error ocurred
  // ...
});

実際にこのまま実行してみると400エラーが出るはずです。
ドキュメントをよく見てみると、こんなことが

どうやら最近サインインした(認証を行った)人のみしか削除できなさそう。

🌵再認証する必要があるけど公式のコードが詳細に書いてない

↑のドキュメントのユーザーを再認証するにある通りに書くと

import { getAuth, reauthenticateWithCredential } from "firebase/auth";

const auth = getAuth();
const user = auth.currentUser;

// TODO(you): prompt the user to re-provide their sign-in credentials
const credential = promptForCredentials();

reauthenticateWithCredential(user, credential).then(() => {
  // User re-authenticated.
  deleteUser(user).then(() => {
     // User deleted.
  }).catch((error) => {
     // An error ocurred
     // ...
  });
}).catch((error) => {
  // An error ocurred
  // ...
});

多分こんな感じになる。
けど、credentialを作るところの関数promptForCredentials()部分が何も書かれていなくて、どうやら自分でcredentialを作る処理を書く必要がありそうです。
ちなみにcredentialはユーザーのアカウント情報がつまっているもののことらしい。

🌵credentialの作り方としてEmailProviderパターンの情報しかない

https://stackoverflow.com/questions/37811684/how-to-create-credential-object-needed-by-firebase-web-user-reauthenticatewith

どうやってcredential作るのってなったときに検索してみると↑のようにEmailProvider(メールとパスワード認証)の情報が多く、GoogleAuthPrivider(Google認証)の情報がほとんどありませんでした。
まあでも↑みたいな感じでやるのね~って思い、EmailProviderをGoogleAuthProviderにして引数の情報を見てからidトークンかアクセストークンを入れればよいとわかって

if (currentUser) {
    const credential = GoogleAuthProvider.credential(
      await currentUser.getIdToken(true),
      null
    );
...

こんな感じで第1引数にidトークンを入れて第2引数のアクセストークンはnullにして実行してみましたが、invalid id_token in idp responseというエラーが出てidトークンが無効ですと言われてしまいました。
アクセストークンの方をうまいこと入れてみても変わりませんでした。

正解はこれだった!!

ここで使いたいcredentialは、再認証した状態の最新のユーザー情報なのでもう一回ポップアップを通して再認証した状態からうまいこと取得できればいいのか?と思って、ログイン時の認証はどうやって処理を書いてたのか見直してみました。

export const login = (): Promise<UserCredential> => {
  const provider = new GoogleAuthProvider();
  return signInWithPopup(auth, provider);
};

よく見たら戻り値がPromise<UserCredential>であり、credentialじゃん!ってなって、このlogin()を実行した後のreturnに今までの処理を繋げる形で書いてみました。

export const login = (): Promise<UserCredential> => {
  const provider = new GoogleAuthProvider();
  return signInWithPopup(auth, provider);
};

const auth = getAuth();
const currentUser = auth.currentUser;

if (currentUser) {
-    const credential = GoogleAuthProvider.credential(
-      await currentUser.getIdToken(true),
-      null
-    );
   // 最近サインインしていないとエラーになってしまうので、再認証してクレデンシャルを取得
    login().then((result) => {
      const credential = GoogleAuthProvider.credentialFromResult(result);
      reauthenticateWithCredential(currentUser, credential!)
        .then(() => {
          // User re-authenticated.
          deleteUser(currentUser)
            .then(() => {
              // User deleted.
              if (user?.id) {
                deleteDoc(doc(db, "users", user.id));
              }
              router.push("/");
            })
            .catch((error) => {
              // An error ocurred
              return {
                isSuccess: false,
                errorMessage: "アカウントの削除に失敗しました",
              };
            });
        })
        .catch((error) => {
          // An error ocurred
          return {
            isSuccess: false,
            errorMessage: "何らかのエラーが発生しました",
          };
        });
    });
 }

これでエラーも出ず、アカウント削除できるようになりました!!

↓このドキュメントにポップアップを出して認証した状態からcredentialを取得するコードがあったので参考にしました。
https://firebase.google.com/docs/auth/web/google-signin?hl=ja

終わりに

筆者はこのアカウント削除処理に5時間くらい沼ったので、同じく沼りそうな方が沼りかけで済んだし、何なら秒で解決して空飛んだわ!くらいまで手助けになっていたら嬉しいです🦧

Discussion