firebaseのupdateEmail()が400エラーを返す原因と対処法

updateEmail()でfirebaseに保存したアカウントのメールアドレスを変更しようとしたら、400エラーが出て一向に成功しませんでした。その原因と対処法をメモしておきます。

結論

最終ログインから時間が経っているとセキュリティの観点から直接メールアドレスの変更はできないようで、reauthenticateWithCredential()で再度ログイン認証を行えばOK!

エラー状況

  • updateEmail()関数でユーザーのメールアドレスを変更できるようにしたい
  • 関数自体はシンプルで間違えてなさそうである
  • 400エラーが出ている

公式の参考コードをほぼそのまま使っていて、ぱっと見何が原因かが分かりませんでした。

原因

アカウント情報を変更する等の処理を行う際、ユーザーが最近ログインしている必要があるようです。(最近の定義は不明)

アカウントの削除メインのメールアドレスの設定パスワードの変更といったセキュリティ上重要な操作を行うには、ユーザーが最近ログインしている必要があります。ユーザーが最近ログインしていない場合、このような操作を行うと失敗し、エラーになります。このような場合は、ユーザーから新しいログイン認証情報を取得して reauthenticateWithCredential に渡し、ユーザーを再認証します。次に例を示します。

ユーザーを再認証する|firebase

今回はテスト用としてずっとログインしたままのアカウントを使用していたので、このエラー原因に該当しました。

対処法

再度ログイン認証を行う

reauthenticateWithCredential()を使ってupdateEmail()の前に再度ログイン認証処理を行います。

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.
}).catch((error) => {
  // An error ocurred
  // ...
});

引用元:Firebase でユーザーを管理する

reauthenticateWithCredential()を使って再認証処理を行います。この時、credentialに代入する情報は個別に用意する必要があるようです。(公式さん、例を載せておいて欲しいです!)

調べたところ、EmailAuthProviderを使えばよさそうでした。要するにメールアドレスとパスワードが分ければOKです。(メールアドレスとパスワード認証でログインしている場合)

認証情報を用意する

//Firebase 8.x
//The credential object is created like so:

const user = firebase.auth().currentUser;
const credential = firebase.auth.EmailAuthProvider.credential(
    user.email, 
    userProvidedPassword
);
// Now you can use that to reauthenticate
user.reauthenticateWithCredential(credential);
//Firebase 9.x
//(Thanks @Dako Junior for his answer that I'm adding here for exhaustivity)

import {
    EmailAuthProvider,
    getAuth,
    reauthenticateWithCredential,
} from 'firebase/auth'

const auth = getAuth()
const credential = EmailAuthProvider.credential(
    auth.currentUser.email,
    userProvidedPassword //ユーザーからパスワードを入力してもらう
)
const result = await reauthenticateWithCredential(
    auth.currentUser, 
    credential
)
// User successfully reauthenticated. New ID tokens should be valid.

引用元:How to create “credential” object needed by Firebase web user.reauthenticateWithCredential() method?

userProvidedPasswordはユーザーからパスワード入力を求める必要があるので、メールアドレス変更フォームにパスワードを入力する項目を追加して、再度メールアドレスの変更処理を行うよう求めます。

パスワードなのでユーザー側の入力は避けられず、最初からパスワード欄を用意しておくか、エラーが出た際にパスワードを要求する仕様にするかは要検討かなと思います。

コード整理(v9版)

v9のfirebaseを使っているので、v9用のコードをまとめておきます。

import {
    EmailAuthProvider,
    getAuth,
    reauthenticateWithCredential,
} from 'firebase/auth'

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

const credential = EmailAuthProvider.credential(
    user.email,
    userProvidedPassword
)
reauthenticateWithCredential(user, credential).then(() => {
  // User re-authenticated.
  console.log("re-authentication succeed")
}).catch((error) => {
  // An error ocurred
  // ...
});

この後にupdateEmail()でメールアドレスの変更処理を行なったところ、無事エラーが出ずに更新されました。また、確認メールの送信等の処理は特に問題が出なかったので、記事が長くならないようここでの記述は控えます。

まとめ

ちょっと面倒ですが、確かにセキュリティ上の問題はあるのでこうした処理は必要かなと思いました。無事対処できてよかったです。

参考

Firebase でユーザーを管理する
How to create “credential” object needed by Firebase web user.reauthenticateWithCredential() method?

ー この記事をシェアする? ー

この記事にコメントする

このサイトはreCAPTCHAとGoogleによって保護されています。プライバシーポリシー利用規約が適用されます。