ハコヅミを作る #51:ベータ版を始めようとしたら、「招待制」が守られていなかった
前回(#50)で、レビューで見つかったものを直し終えました。残りの準備を済ませて、ベータ版を始める日です。
- 残りの準備:データを戻す手順と、入口を1つにすること
- 守りを外して試したら、招待していないアカウントで入れた
- Google の「テスト中」は、何を守っているのか
- アプリの側に、招待の一覧を作る
残りの準備
ベータ版の前に、2つを済ませました。
データを戻す手順
データベースには、過去の時点に戻す仕組みがあります。ただ、#49 で書いたとおり、戻すと、そのあいだに退会した人のデータまで戻ってきます。
そこで、退会した人を見分ける番号と、退会した日時だけを、7 日のあいだ、データベースとは別の場所(Cloudflare の KV)に残すことにしました。データベースと同じ場所に置くと、戻したときに、この記録も一緒に戻ってしまうからです。
手順書を書いて、開発環境で、実際に戻すところまで試しました。「戻せるはず」と「戻したことがある」は、違います。
入口を1つにする
本番には、独自ドメインのほかに、Cloudflare が自動で付ける URL が2種類ありました。ログインは独自ドメインからしかできないので、残す理由がありません。設定で閉じました。
ここまでは、予定どおりです。
今までの守りと、外す順番
今までの本番は、2つの関門で守っている、と考えていました。
- Cloudflare Access:自分しか、URL を開けない
- Google のログイン:Google の設定が「テスト中」で、登録した人(テストユーザー)しかログインできない
ベータ版では、1 だけを外します。2 が、招待制の関門になります。テスタから応募が来たら、Google のテストユーザーに足す。足していない人は、Google の画面で断られる。そういう設計でした。
紹介のページにも、「今はベータ版で、招待した方だけがログインできます」と書いてあります。
手順書には、外した直後に確かめることを並べてありました。その中の1行が、これです。
テストユーザーに登録していない Google アカウントで、ログインを押すと、
Google の「アクセスをブロック」の画面になる(ログインできない)
入れてしまった
Access を外して、順に確かめました。紹介のページが開く。ログインが要る画面は、ログインの画面に移る。応答のヘッダーも付いている。
残ったのが、上の1行です。テストユーザーでない Google アカウントを、持っていませんでした。そこで、自分の2つ目のアカウントをテストユーザーから外して、試しました。
ログインできました。
最初は、前に許可した記録が Google の側に残っているのだろう、と考えました。Google のアカウントの設定から、ハコヅミとのつながりを消して、もう一度。
ログインできました。
Access を戻して、入口を閉じます。ここまで、外していたのは数十分です。どこにも知らせていないので、知らない人が登録した様子はありません。
原因を、1つずつ消す
一緒に作っている Claude Code と、原因を絞りました。
| 疑ったこと | 確かめ方 | 結果 |
|---|---|---|
| 見ている Google Cloud のプロジェクトが、本番のものと違う | 本番のログインが Google に渡している番号と、プロジェクトの番号を比べる | 同じ |
| 公開ステータスが「本番」になっている | Google Cloud の画面 | 「テスト中」 |
| 試したアカウントが、プロジェクトのメンバー | メンバーの一覧 | 入っていない |
設定は、全部正しい。それでも入れる。
ここで Claude Code が、Google の公式のヘルプを読みに行きました。答えは、そこに書いてありました。
「テスト中」のアプリは、テストユーザーしか使えない。ただし、アプリが求めるのが、名前・メールアドレス・プロフィールだけのときは、例外。テストユーザーの一覧にいなくても、使える。
ハコヅミが Google に求めているのは、ちょうどこの3つだけです。つまり、ふつうの「Google でログイン」には、テストユーザーの制限は、最初から効きません。
招待制の関門は、なかったことになります。
なぜ、気づかなかったか
振り返ると、気づく機会は2回ありました。
1回目は、設計のときです。「Google のテストユーザーで、招待制にする」と決めました。Google の画面には、「テスト中の間は、テストユーザーのみがアプリにアクセスできます」と書いてあります。それを読んで、そのとおりだと思いました。例外は、別のページに書いてあります。
2回目は、#48 のレビューです。別のセッションの AI が、こう指摘していました。
「招待した人だけ」を守る仕組みが、コードにない
このとき、「守っているのは Google の設定で、コードの外にある」と答えて、直さないと決めました。指摘は、正しかったわけです。コードの外にあると思っていた守りを、誰も実物で確かめていませんでした。
助かったのは、手順書に「登録していないアカウントで、断られることを確かめる」と書いてあったことです。そして、その行を飛ばさなかったことです。別のアカウントを持っていなかったので、飛ばしたくなる行でした。
アプリの側に、招待の一覧を作る
直し方は、3つ考えました。
| 案 | 中身 |
|---|---|
| A | 招待した人のメールアドレスの一覧を持ち、初めてログインする人が一覧にいなければ、断る |
| B | 招待の一覧をデータベースに持ち、管理画面から足す・外す |
| C | 招待制をやめて、誰でも登録できるベータ版にする |
A にしました。作るものが小さく、一般に公開するときに、外すだけで済みます。
一覧の置き場
最初は、サーバーの秘密の設定値に、メールアドレスを並べるつもりでした。Issue の下書きを書いている途中で、Claude Code が弱点に気づきました。秘密の設定値は、入れたあとに中身を見られません。テスタを1人足すたびに、全員分を入れ直すことになります。
代わりに、Cloudflare の KV に、1人を1つのキーで置くことにしました。ダッシュボードで、1行足す・消す・見る、ができます。メールアドレスを、ソースコードのリポジトリに入れずに済みます。
断る場所
ログインに使っている Better Auth には、ユーザーを作る直前に呼ばれる入口がありました。ここで「だめ」と返すと、ユーザーもセッションも作らずに、ログインの画面へ戻してくれます。
決めたことは、次のとおりです。
- 確かめるのは、初めてログインする(ユーザーを作る)ときだけ。もう登録している人は、確かめない
- 一覧にキーがない、一覧を読めない、Google が確認済みとしていないメールアドレス。どれも、断る
- 一覧が空なら、誰も新しく登録できない
迷ったら、断る側に倒します。

招待していないアカウントでログインしようとしたときの画面
退会したら、招待も消す
一覧を持つと、新しい問題が1つ出ます。プライバシーポリシーには、「退会すると、すぐに消えます」と書いてあります。退会した人のメールアドレスが、招待の一覧に残ったままでは、約束に例外が増えます。
退会のときに、招待の一覧からも消すことにしました。もう一度使うには、招待し直しです。退会の確認の画面にも、そう書きました。
消す順番は、招待の一覧を最初にします。ここで失敗したら、まだ何も消えていないので、やり直せます。招待だけ消えて、あとが失敗しても、困りません。もう登録している人は、招待を確かめないからです。
今度は、実物で確かめる
同じまちがいを、繰り返さないようにします。
自動テストは、Better Auth を実際に動かして、「一覧にいない人は、ユーザーができない」ことを確かめています。直す前のコードで流すと、ちゃんと落ちることも見ました。
ただ、それだけでは足りません。今回の原因は、「実物で確かめていなかった」ことです。開発環境に出したあと、本物の Google を通して、手で確かめました。
- 自分のアカウントで、退会する
- 招待の一覧から、自分のキーが消えているのを見る
- もう一度ログインする → 「このアカウントは、招待されていません。」
- キーを足し直す → ログインできる
本番でも、同じことを、Access を付けたまま確かめました。守りが効いているのを見てから、入口を開けます。今朝は、順番が逆でした。
おわりに
- 外のサービスの設定に守りを任せるなら、条件に合わないもので、実際に断られることを確かめる。画面に「〜のみがアクセスできます」と書いてあっても、例外は別のページにある
- 「守りはコードの外にある」と答えたくなったら、その守りを、誰がいつ確かめたかを思い出す
- 確かめるのが面倒な行ほど、飛ばさない。飛ばしたくなる行に、いちばん大事なことが書いてある
- 入口を開ける前に、守りを確かめる。開けてから確かめると、確かめているあいだ、開いている
- 迷ったら、断る側に倒す。一覧が空なら、誰も入れない
- 約束(退会すると消える)は、あとから足したものにも、守る
今日のうちに始めるつもりだったベータ版は、半日遅れました。それでも、テスタを招く前に見つかって、よかったと思っています。
次回は、あらためて、ベータ版を始めます。