Firebase Auth:アプリを完全に終了すると、匿名アカウントが量産された(currentUser の復元待ち)
トレカダッシュ 開発の記録 / 2026年10月
iOS アプリ「トレカダッシュ」で、「アプリを一度落とすと、また Apple のサインインを求められる」という報告がありました。調べると、19 人のうち 17 人が、作られては捨てられた匿名アカウントでした。原因は、アプリを起動した直後の、たった 1 行の確認のしかたでした。HANDOFF とコミット(2026年8月14日)の記録で書きます。
このサイトには、トレカダッシュの作品ページはまだありません。ここに書くのは、開発の記録です。
報告された症状
「1 回アプリを落とすと、なぜか、また Apple のサインインが求められる」。聞き取ると、条件がありました。スワイプで完全に終了して、起動し直したときだけ起こります。ホームボタンで離れて戻るだけなら、起きません。
実データを見る
Firebase の管理画面で、認証(Authentication)のユーザー一覧を、実際に開いてみました。結果は、こうです。
| ユーザー | 人数 |
|---|---|
| 全体 | 19 |
| Apple のサインインが済んでいる | 2 |
| 使い捨ての匿名アカウント(作成した日とログインした日が同じ) | 17 |
17 件のうち、8 月 13 日だけで 10 件。アプリを起動し直すたびに、新しい匿名アカウントが、作られていたことになります。
原因:「まだ戻っていない」ものを、同期的に見ていた
アプリは、起動した直後に、「いまサインインしている人がいるか」(Auth.auth().currentUser)を、その場で確認していました。いなければ、匿名アカウントを新しく作る、という動きです。
ところが、Firebase の公式の説明にあるとおり、currentUser は、起動の直後には、保存場所(Keychain)からの復元が終わっているとは限りません。復元が間に合わないと、「誰もサインインしていない」と見えてしまいます。すると、本当は Apple のサインインが済んだ既存の状態が、もうすぐ復元されるところなのに、その直前に、新しい匿名アカウントを作ってしまう。競争(レースコンディション)です。
この競争が起きるのは、完全に終了してからの起動(冷起動)だけです。バックグラウンドから戻るときは、Firebase の初期化そのものが走り直さないので、起きません。報告の条件と、ぴったり合います。
直し方
- 同期的な確認をやめる。復元が終わったことが確定するのは、状態の変化を知らせる仕組み(
addStateDidChangeListener)の、最初の呼び出しのときです。判定を、その時点だけにしました。 - 保険を一つ足す。アカウントを管理する側(
AccountStore)でも、起動時に、サーバー側の実際の状態と突き合わせて(reload())、手元の記録とずれていれば、補正して、診断のログを残すようにしました。
もう一つ、分かったこと
この調査の途中で、別の事実も分かりました。Apple のサインインは、実装した 8 月 7 日から、このときまで、実機で一度も成功していませんでした。
- 最初のエラーは、「サインインの方式の設定が見つからない」。Firebase の管理画面で、Apple の方式そのものが、一度も有効にされていませんでした。
- 有効にして再挑戦すると、今度は「すでに連携されている」というエラーが出ました。有効にする前の試行が、実は途中まで成立していて、この端末の匿名アカウントに、Apple ID がもうリンクされていたのです。アプリは、これを「エラー」と表示していましたが、実質、サインイン済みです。成功として扱うように直しました。
確認できていないこと
iOS 側の変更なので、TestFlight に届くまでは、実機で効果を確かめられません。記録には、「実機で、完全に終了して起動し直すことをくり返しても、Apple の連携が保たれるかを、最優先で確認する」と書いてあります。その結果は、この記事を書いた時点で、記録にありません。
分かったこと
「起動した直後」の状態は、まだ、確定していない。
- 「まだ戻っていない」と、「ない」は、別のもの。復元を待たずに、「ない」と決めると、新しいものを作ってしまいます。
- 症状は、実データに出る。管理画面を開いたら、17 件の使い捨てが並んでいました。「落とすとサインインが消える」を、手元の再現だけで追っていたら、見つけにくかったはずです。
- 「一度も成功していない」ことは、成功の記録が無いことで、分かる。登録済みの 5 アカウントに、Apple の情報が 1 つも無かったことが、手がかりでした。
この記事は、HANDOFF とコミット(2026年8月14日)の記録から書きました。
この話に出てくるもの
- 初めての審査で、「情報が必要です」と返ってきた — 同じアプリの、審査の記録(アカウント削除も)
- 「取れた」と読んでいたのは、ただの案内ページだった — 同じアプリの、別の記録
ほかの記録
- 「重複は実質ゼロ」と数えたのに、監査は112組を出していた — 重複を統合する処理が、毎晩、先頭のジャンルの途中で時間切れになっていた。「ゼロ」と数えた自分の測り方が間違っていた。持ち回りに直して112組を0組にした記録。
- 「取れた」と読んでいたのは、ただの案内ページだった:3日間、商品が0件 — ポケモンセンターの取得が3日間ずっと商品0件だった。安い設定が返した2,410バイトの「JavaScriptを有効に」ページを、成功と読んでいた。判定を1か所に集めた記録。
- 確認できなかったときに、通していた:「fail-open」が転売品を招いた話 — 「定価を確認できなかったら通す」という設計に、検索の1日上限という盲点があった。「知らなかった」と「調べられなかった」を区別して直した記録。
コメント
感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。
押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。