Ted

私が運用するサイトのサインアップでは、メールアドレスの所有権を証明するよう求め、その後もう一度求めました。

最初は6桁のコードで、メールで送られ、入力します。それが完了してから初めてアカウントが作成されます。その後、作成時に認証サービスが独自の確認メールをリンク付きで送信しました。

2通のメール。1つのアドレス。同じ質問が2回尋ねられたのです。

これは徹底しているように見えました。実際にはデータの分岐点であり、すでに本物のユーザーをシステムが適切な答えを持たない場所に置いていました。

誰も設計しなかった状態

誰かがサインアップしました。コードを受け取り、正しく入力し、アカウントが作成されました。アプリは書かれているとおり、直ちにサインインさせました。

その人は2番目のリンクをクリックしませんでした。なぜクリックするでしょうか。受信箱からコードを入力したばかりで、今ログインしたページを見ているのです。利用者からすれば、仕事は完了したのです。

したがって、レコードにはこうあります。アカウントが存在し、パスワードが設定され、セッションが有効で、サインイン済み——そして email_confirmed_at は null。

この行はサインアップが半分完了していないことを示しています。完了したサインアップに加え、誰も必要とせず、何も強制しなかった確認の残り物フラグを示しています。しかし「メール確認済み」という名前のフラグが「いいえ」を示していると、いずれ何かに読み取られます。サポートツール、監査、確認を条件とする将来の機能はいずれも、そのユーザーを確認していないと判断します。

しかし、6週間前に確認済みです。別のテーブルにその人の名前で使われたコードが存在します。

2つのレコード、1つの事実

これが、私が恥ずかしいほど時間がかかった再解釈です。

私は2回目の確認を冗長性——ベルトとサスペンダー、害はない——と考えていました。違います。それは同じ事実が記録される2つ目の場所であり、1つの事実の2つのレコードは最終的に一致しなくなる。

事実は「この人がこのアドレスを管理している」です。OTPテーブルは、使用済みコードの形でこれを知っています。認証サービスはタイムスタンプの形でこれを知っています。2つを同期させるものは何もありません。なぜなら、それらは最初から1つのものとして設計されたわけではなく、1つはアプリケーションロジックで、もう1つはデフォルトのままにされたプラットフォームの機能だからです。

ユーザーが一方を満たし、もう一方を満たさない瞬間、事実は2つの値を持ちます。そして、事実が2つの値を持つとき、下流のすべてのコンシューマーはどちらを信じるかを選ばなければならず、通常は選択があることに気づきません。

何も強制していなかった方

2回目の確認が本物で、1回目が便宜的なものであるバージョンもあります。それなら少なくとも一貫性があるでしょう。

しかし、そうではありませんでした。リンクは何も強制しませんでした。

ユーザーは、確認メールが開かれる可能性のある時間より前にサインインしていました。なぜなら、アプリはアカウント作成直後にサインインを直接呼び出すからです。サインインはフラグを参照しませんでした。製品のどの部分もフラグを参照しませんでした。アカウントは、リンクがクリックされたかどうかにかかわらず、まったく同じように動作しました。

したがって、2回目の確認は何もブロックできず、何もゲートできず、ユーザーが気づくような失敗の仕方もありませんでした。その観測可能な唯一の効果は、ある列を設定することであり、それが同じ事実のもう1つのレコードと矛盾することでした。

これが適用すべきテストです。確認が何も拒否できないなら、それは確認ではありません。意見付きのログエントリです。

なぜオンになっていたのか

誰もこれを選んだわけではありません。確認メールはプラットフォームのデフォルトであり、理にかなったデフォルトです——サインアップが「アカウントを作成し、リンクをメールで送る」というアプリの場合です。それが設計された流れです。

私たちはその流れを独自のものに置き換え、古い方をオフにしませんでした。元のコードでさえコメントでそう述べていました。

// Create the user account with auto-confirm (since we verified email via OTP)

Enter fullscreen mode Exit fullscreen mode

意図は記録されていました。それが参照していた設定は変更されませんでした。したがって、コメントは存在しないシステムを説明しており、問題を引き起こした行のすぐ上に、何ヶ月もの間配置されていました。

私はそのコメントに多くの共感を持っています。これは、置き換えを構築し、置き換えられたものが脇に退いたと合理的に仮定したときに起こることです。

修正と、すでに壊れていた行

プラットフォームの確認をオフにするのは1つの設定でした。新しいサインアップは今、正確に1通のメールを受け取り、正確に1つのコードを含み、単一の一貫した事実のレコードでサインインした状態になります。

取り残されたユーザーには別個の決定が必要で、それは見た目より興味深いものです。選択肢は以下の通りでした。

  • 未確認のフラグを残す——これは誤り。
  • フラグを現在に設定する——これも誤り。今日所有権を証明したと述べているが、そうではない。
  • 実際にコードを入力した瞬間に設定する。

私は3番目を選びました。彼らの確認タイムスタンプは今、アカウント作成の33秒になっており、これはデータエラーのように見えますが、実際には入手可能な最も正確な記述です。彼らはそのアドレスを所有していることを証明し、その後アカウントが作成されました。

私は、奇妙に見えて真実であるタイムスタンプを、整然と見えて真実でないものよりも好みます。その行を調査する人は誰でも、対応する使用済みコードが別のテーブルにすぐそこにあることを見つけ、順序が理解できるでしょう。

次回、より早く尋ねるべきこと

プラットフォームの流れを独自のものに置き換えるとき、作業は自分のものが動作した時点で終わるわけではありません。プラットフォームのバージョンがオフになった時点で終わります。

それまでは、確認済みメールアドレスを持っているわけではありません。異なるシステムによって維持され、偶然一致している確認済みメールアドレスについての2つの主張を持っているのです——そして、偶然は、ユーザーが受信箱を1回ではなく2回読むという、まったく合理的なことをするまで成立します。