
Googleログインとゲストプレイでつまずいた
こんにちは、よなです。
YONA GAMESという個人ゲームサイトを運営しています。
前回は、Cloudflare Pagesへの初回デプロイで、ローカルでは動いていたサイトが本番だけ動かなかった話を書きました。build:production が存在しない。環境変数が足りない。画像の参照先が違う。公開作業は、エラー画面をひとつずつ潰していく作業でした。
今回の主役はログインです。「ログインさせるだけ」なら簡単そうに見えませんか?私も最初はそう思っていました。実際には、ログインという一言の裏に、いくつもの小さな仕組みが層のように重なっていました。
ログインを必須にしたくなかった
最初に考えたのは、Googleログインをしてもらってからゲームを始める流れです。認証が済んでいれば、セーブデータもユーザーごとに管理しやすい。
ただ、初めて来た人にとっては、ゲームを遊びたいだけなのにログインを求められることになります。個人サイトなのに、ゲームプレイするにはGoogleのメール認証必須って言われたら、ちょっと怖いって思いませんか?MINECOIN Minerは、PCブラウザで遊ぶ無料ゲームです。できれば、開いてすぐ遊んでほしいですし、最初の門でユーザーを回れ右させたくありません。
そこで、ログイン方法を二つに分けました。
ゲストプレイ:ログインなしで開始
Googleログイン:アカウントに紐づけてプレイゲストプレイでは、Supabaseの匿名ログインを使います。見た目にはログインしていませんが、内部的には匿名ユーザーを作成し、そのユーザーIDにセーブデータを紐づける仕組みです。
ただし、誰でも無制限に匿名アカウントを作れる状態にはしたくありません。Botによる大量作成を防ぐため、Cloudflare Turnstileも組み合わせることにしました。
ゲスト開始
↓
Turnstileで確認
↓
Supabase匿名ログイン
↓
セーブデータ作成図にすると、そこまで複雑には見えません。でも、図が単純なことと、実装が単純なことは、まったく別の話です。この一つひとつが、別々のサービスによる別々の処理なんです。
ゲストで始めたいのにCAPTCHAで止まる
最初のゲスト開始を試したとき、いきなりエラーが出ました。
captcha protection: request disallowed (no captcha_token found)画面にはこう表示されます。
Turnstileを読み込めませんでした。ログインを必須にしたくないからゲストプレイを用意したのに、遊び始める前にCAPTCHAで止まる。入口を広くしたつもりが、入口の手前に検問所を新設していたようなものです。ゲストプレイという名の「面倒な手続きなしで入れます」という看板の裏で、しっかり手続きを要求していました。
原因は、Supabase側の匿名ログインでCAPTCHA保護が有効になっている一方、リクエストに必要な captcha_token が届いていなかったことでした。Turnstileを画面に表示するだけでは足りません。ユーザーがチェックを通過したあと、その結果をトークンとして受け取り、Supabaseの認証処理へ渡す必要があります。
画面にウィジェットがある。チェックマークが表示される。それだけで認証まで繋がっていると思い込んでいませんか?私は思い込んでいました。実際のリクエストの中身を見ると、肝心のトークンが乗っていない。前回のデプロイ記事で「画面を見ただけでは本番確認にならない」と書きましたが、今回もまったく同じ構図でした。見た目ではなく、実際に送られている値を確認する必要があります。
Turnstileが読み込めない
さらに、Turnstile自体が読み込めない状態も発生しました。表示されるメッセージは先ほどと同じです。
Turnstileを読み込めませんでした。この時点では、何が原因なのかすぐにはわかりませんでした。Cloudflare側の設定なのか。本番環境変数なのか。スクリプトの読み込みなのか。それともSupabaseの認証設定なのか。
一つの画面に表示されたエラーの裏に、関係している場所が複数ある。犯人が一人だと思って捜査していたら、実は複数の容疑者が別々の理由で怪しい動きをしていた、というやつです。
ローカル環境では開発サーバーが動いていて、必要なスクリプトも問題なく読み込めます。しかし本番では、ドメインも配信パスも環境変数も変わる。Cloudflare Pagesへ接続したあと、あらためて本番URLで確認する必要がありました。
最終的にゲストプレイの動作自体は問題なさそうと判断できるところまで確認しましたが、この時点で「認証画面が出たから成功」とはもう考えなくなりました。ゲスト開始から盤面へ進めること。セーブデータが作られること。ページを更新しても続きから遊べること。そこまで確認して、ようやくゲストプレイが動いたと言えます。
次はSupabaseの設定で止まる
ゲストプレイが動き始めたあと、Googleログインとの連携を試しました。ゲスト状態からGoogleアカウントへつなげられれば、最初はログインなしで遊び、必要になったらアカウントを連携できます。利用者にとっては便利な流れのはずでした。
しかし、ここでもエラーです。
Manual linking is disabled今度はCloudflareではありません。Supabase側で、ユーザーの手動連携がそもそも無効になっていました。
アプリ側にGoogleログインのボタンを作っても、Supabase側が連携を許可していなければ動きません。コードを書いてボタンを配置したのに、裏側の管理画面で「連携禁止」のスイッチが入ったままだった。玄関のドアを開けたのに、大家さんが建物全体に鍵をかけていた、みたいな話です。
Supabase側で手動連携を有効にして、もう一度試します。今度はGoogleアカウントへ進めるようになりました。
Googleログインは動いたけど確認は一筋縄ではいかない
Google OAuthのリダイレクトURLも本番用に設定しました。ログイン後にどこへ戻るのか。開発環境と本番環境でURLが変わっていないか。シークレットウィンドウでも動くか。確認することは、思っていたより多くありました。
自分のブラウザでは、すでにログイン状態が残っていることがあります。その状態で「ログインできた」と判断すると、初めて訪れた人の動作を確認したことにはなりません。
そこで、次の環境でテストしました。
ログアウト状態
別ブラウザ
シークレットウィンドウ
Googleログイン後に通常プレイできること。同じGoogleアカウントで、既存のゲームデータを取得できること。ログイン後もゲームを続けられること。ここまで確認できたので、Googleログインそのものは動いていると判断しました。
でも「続きから遊べる」とは限らなかった
ここで、また別の問題が見えてきました。ゲストプレイ中のデータを、あとからGoogleアカウントへ移行するケースです。
ゲストで遊んでいる間は、匿名ユーザーのIDにセーブデータが紐づいています。Googleログインをすると、今度はGoogleアカウントに紐づくユーザーIDが関係してきます。つまり、単純にログイン状態を切り替えるだけでは、次のようなことが起こります。
ゲスト中のセーブデータ
↓
匿名ユーザーIDに紐づいている
Googleログイン後
↓
Google側のユーザーIDを見る
結果
↓
初期状態に見える実際に、ゲストプレイ中のデータをGoogleアカウントへ移行した際、データが引き継がれず初期状態になる事象が発生しました。
ログインは成功している。Googleアカウントも認識している。でも、プレイヤーから見れば「攻略済データが消えた」ように映る。これは、かなり冷や汗の出る状態です。認証に成功したことと、セーブデータを正しく引き継ぐことは、まったく別の問題でした。
内部ユーザーIDだけでは解決しない問題
前回の記事では、メールアドレスやOAuthトークンをゲーム用データベースに保存せず、Supabaseの内部ユーザーIDだけを使う方針を書きました。この方針自体は、今でも正しいと思っています。
ただ、内部ユーザーIDを使えば、すべてのデータ移行が自動的に解決するわけではありませんでした。ゲスト時のIDと、Google連携後のIDは同じユーザーとして扱われるのか。匿名ユーザーに紐づいたセーブデータを、どのタイミングで移すのか。移行に失敗した場合、元のデータをどう守るのか。考えるべきことは、むしろ増えました。
「メールアドレスを保存しておけば、簡単に紐づけられたのでは」と一瞬よぎります。でも、それは前回捨てたはずの問題を、わざわざ拾い直しに行くようなものです。メールアドレスはゲームデータの識別子として扱わない。OAuthトークンも保存しない。そのうえで、ゲストから正式アカウントへ移るときのデータ移行を、別途きちんと設計する。安全にするために持たないことと、データを失わないこと。この二つを、同時に成立させる必要がありました。
認証まわりでわかったこと
今回わかったのは、「ログイン機能」という一つの言葉の中に、実は複数の別々の仕組みが同居しているということです。
Turnstileを読み込む
CAPTCHAの結果を受け取る
captcha_token をSupabaseへ渡す
Supabase匿名ログインを実行する
Google OAuthへリダイレクトする
戻り先のURLを正しく設定する
手動連携を有効にする
ゲストデータを正式アカウントへ引き継ぐ
どれか一つが成功しても、全体が成功したとは限りません。Turnstileが表示されても、認証リクエストにトークンが含まれているとは限らない。Googleログインが成功しても、ゲスト時のセーブデータが表示されるとは限らない。認証画面を通過したことは、ゲームを続きから遊べることの証明にはならない。この現象を通して、何度も同じ壁にぶつかっている気がします。
一番の教訓
今回の教訓は、「ログインできる」と「その人のデータで遊べる」は、まったく別のテスト項目だということです。
ゲストプレイを用意したことで、ゲームを始めるまでのハードルは下がりました。一方で、あとからGoogleログインへつなぐなら、ユーザーIDとセーブデータの移行までちゃんと設計する必要があります。入口を軽くした分だけ、出口の設計が重くなった。そんな感覚です。
サービスを組み合わせると、それぞれの設定を終わらせるだけで全体が動くように思えます。でも実際には、サービスとサービスの継ぎ目にこそ問題が潜んでいました。TurnstileとSupabaseの間。Supabase匿名ログインとGoogleログインの間。匿名ユーザーIDと正式ユーザーIDの間。部品は単体では正常に動いているのに、部品と部品を繋いだ瞬間だけ動かなくなる。今回も、ゲーム本体の盤面より、認証まわりのほうがよっぽど地雷だらけでした。
ゲストプレイとGoogleログインは、本番環境で動作確認できました。一方で、ゲスト時のデータ移行については、引き継がれず初期状態になる事象が残りました。認証機能が完成したと思った瞬間、データ移行という次の課題が顔を出してきたわけです。
公開作業は、エラーを直して終わりではありません。直したあとに、別の利用者の流れで試す。ログアウトして試す。別ブラウザで試す。ゲストで始めて、あとからログインして試す。そのたびに、また別の地雷が見つかります。それでも、実際に誰かが遊び始める前にここまで見つけられたことは、幸いだったと思っています。
当たり前のようですが、ゲームを作ることとゲームを公開することはまったく別の作業です。そして、認証を実装することと、プレイヤーが安心して続きから遊べることも、また別の作業が必要でした。
ではまた!
PC専用ブラウザゲーム『MINECOIN Miner』絶賛公開中!
ソシャゲ風マインスイーパーで鉱石の収集・スキルで素早く盤面攻略・深層探査などのやりこみ要素もあります!ぜひプレイしてみてください。
