前段
Flutter にて firebase_auth を利用して開発中のサービスにて、updateEmail() 関数を用いて email アドレスを更新しようとするも、何故かエラーが出て怒られてしまい変更処理ができなかった。
以下エラー文:
[firebase_auth/operation-not-allowed] This operation is not allowed. This may be because the given sign-in provider is disabled for this Firebase project. Enable it in the Firebase console, under the sign-in method tab of the Auth section. [ Please verify the new email before changing email. ]
要するに、
この操作は認められていません。
firebase console で sign-in メソッド (今回の場合は email/password 認証) を有効にしてください。
って感じです。
当然 firebase console で email/password 認証を有効にしているのでその点は問題無いはず。
他に追加設定が必要なのかと思いくまなく探すも見つからず・・・。
実はこの最後の文章、
[ Please verify the new email before changing email. ]
っていうのがヒントではあったんですが、うーん。なんで出来ないんだ・・・。
と2日くらい試行錯誤していました。
ちなみに事象発生時の firebase 関連のパッケージバージョンはこんな感じ
firebase_core: ^2.24.2
firebase_messaging: ^14.7.10
cloud_functions: ^4.6.0
cloud_firestore: ^4.14.0
firebase_ui_auth: ^1.12.1
firebase_auth: ^4.16.0
firebase_storage: ^11.6.5
結論
updateEmail() を verifyBeforeUpdateEmail() に書き換えることで解決しました!
※ email 変更処理の実装が初めてって方は、注意事項等あるのでもう少し読み進めてください。
final user = FirebaseAuth.instance.currentUser;
try {
+ await user?.verifyBeforeUpdateEmail(newEmail); //こっちに書き換える
- await user?.updateEmail(newEmail); //廃止されたコード
} catch (e) {
log("$e");
return;
}
verifyBeforeUpdateEmail() に置き換えて実行すると、新しく登録したメールアドレス宛にこんなメールが届きます。
このメールの指示通りにリンクをクリックして遷移すると、このようなメッセージが表示されてメールアドレスの変更が完了します。
これでメールアドレスの変更処理は完了です!
※上記の通り処理変更後もエラーが出続ける場合は、一度firebase_authと依存パッケージをアップグレードしてみてください。
flutter pub upgrade --major-versions
それでもなおエラーが出続ける場合は、下記を読み進めてください。
email 変更の実装経験がない方向け
これまでに email 変更の実装経験が無い方は、上記処理を変更するだけでは解決しない可能性があります。
というのも、アカウントの削除、メールアドレスの変更、パスワードの変更等はセキュリティ上リスクが高いので、いくつかの条件をクリアしないと処理がそもそも実行できません。
以下、公式:
重要: ユーザーのメールアドレスを設定するには、ユーザーが最近ログインしている必要があります。ユーザーの再認証をご覧ください。
https://firebase.google.com/docs/auth/flutter/manage-users?hl=ja
ここで、ユーザーの再認証とは、同一ページも書いてありますが下記の処理を指します。
// email, password 等は TextFormField などを用いて取得する必要がある。
final credential = EmailAuthProvider.credential(email: email, password: password);
await user?.reauthenticateWithCredential(credential);
print(FirebaseAuth.instance.currentuser) などをすれば分かる通り、firebase_auth では認証情報として lastSignInTime (最終ログイン日時) 情報を保持しています。
この最終ログイン日時は、email/password 認証情報を入力してログインに成功した場合に書き換えられます。
言い換えれば、ユーザーが同一端末でログインしっぱなしの場合には更新されない値になります。
ユーザーの最終ログイン日時によっては上述した制約に引っかかり、requires-recent-login を含む FirebaseAuthException エラーが投げられます。
その為、予めユーザーの再認証の実装が求められるわけです。
また、email/password 認証でユーザー登録した場合は、メールアドレス変更前までに、現在のメールアドレスを検証が必要となる可能性があります。
await FirebaseAuth.instance.currentUser?.sendEmailVerification();
上記を実行すると、ユーザー登録に用いたメールアドレス宛に確認メールが届くので、メッセージに従ってリンクをクリックして検証を完了させてください。
エラー原因
さて、今回のエラーが発生した原因ですが、公式にヒントがありました。
updateEmail() は 4.17.0 以降で廃止されているとのこと。
https://pub.dev/packages/firebase_auth/changelog
FIX(auth): deprecate updateEmail() & fetchSignInMethodsForEmail() (#12143). (dcfd9e80)
私が最初にこのエラーに遭遇したバージョンは firebase_auth: ^4.16.0 だったので、おそらく 4.17.0 より前から何かおかしな状態になっていたか、firebase のサーバーサイドの処理自体も 4.17.0 リリースのタイミングで変えた為、それ以前の firebase_auth バージョンを使用していたとしても updateEmail() が機能的に使えないようになっていたのかな、と。
(余談ですが、^4.16.0 は他依存関係パッケージと互換性のある、4.16.0 以上のいずれかのバージョンを使用する、という意味です。なので、4.17.0以上のバージョンが選択される可能性もありましたが、確定バージョンを記載する pubspec.lock ファイルには firebase_auth 4.16.0 と書いてあったのでその線は無さそうです。)
公式のこの ChangeLog を見つけて、もしやと思い updateEmail() 関数を verifyBeforeUpdateEmail() 関数に置き換えてみると、エラーを解消できました。
ちなみに、4.17.0 にするまでは表示されませんでしたが、4.17.0以降にアップグレード後、コードエディタ上でも Deprecated と簡単に見分けられるようになりました。
終わりに
公式を見ても他の方の情報を見ても問題無さそうなのにな。。と悩んでいましたが、意外とシンプルな解決方法でした。
これからも Flutter 関連の情報をアップデートしていくのでチェックしてみてください。
何か質問等あればお気軽にどうぞ!