🕵️

ユーザーにユーザー登録していただく前に登録済みのユーザーっぽいことを許したい、そういう気持ちがある。

2023/04/17に公開

はじめに

ポエムです

Webサービス、「登録」してもらうのが一番大変な気がするので、登録する前にユーザーっぽいことを許したい。
という願望を昔抱いたことが有りました。

今回は簡単にvote, content, userの3つの機能を作ってみたいと思ったので、
細かい仕様について思考実験的に書いてみます💖。

仕様

オーソドックスな部分

以下に仕様・用語を記す

テーブル系 ClassName(table_name)

  • User(users)

    • ユーザー実体
    • state: (未認証, 認証済み)
  • Vote(votes)

    • 投票的なもの
    • user_id
    • content_id
  • Content(contents)

    • 投稿されたコンテンツ
    • user_id

使う言葉・仕様

  • session
    • サーバーサイドが発行したcookieに格納されたsession_idに基づく
    • ステートフルな通信を実現するための仕組みという認識
  • ログイン機能などについて
    • email, passwordを /sign_up にPOSTすると、userが作成される
    • email宛に認証メール(activation_token)が送信される
    • 認証メールのリンクをクリックすると、認証が完了する
  • パスワードリセットについて
    • emailを /password_reset にPOSTすると、パスワードリセットメール(reset_token)が送信される
    • パスワードリセットメールのリンクをクリックすると、パスワードリセット画面に遷移する
    • パスワードリセット画面で新しいパスワードを入力すると、パスワードがリセットされ、ログイン画面に遷移する
    • 未認証ユーザーでも行えるようにする

この仕様の問題点

  • ログインしないとcontentの作成やvoteが出来ない
    • つまり登録しないとサービスを楽しめない状態にある
    • サービスが楽しいから登録したいというユーザーの流れあるほうが自然
      • 今回のサービスは、contentを作ってそれにみんなが投票するだけのサービスなので、投票するだけでも楽しめるようにしたい

具体的な対応策

  • Userのstateに(仮)を追加する

    • メールアドレスも何も入力してない状態
    • 仮ユーザーは適当(e.g. 自分が持っているドメイン・サブドメインのメールアドレスに適当にUUIDを着ける)なメールアドレスとパスワードを割り振る
  • state仮のユーザーを作るタイミングについて

    • userに結びついているリソースを作成しようとしているのにもcurrent_uesrが存在しない場合
      • 未ログインでのvote, contentの作成時
      • voteは適当でいいけどcontentは投稿時に何かしらの規約に同意してもらったほうがいいと思う
    • このぐらいの遅延をしないと、usersレコードが莫大になりそうなので…。
  • 仮ユーザーが登録をしようとした場合

    • (後述)統合処理を行う
    • メアドとパスワードを入力したときに、
      新たにusersにレコードを作成しactivation_token付きメールを送る
    • 🆖仮ユーザーのレコードをメアドとパスワードで更新するのは🆖
      • Aさんが仮ユーザー状態で好き勝手やって、Bさんのメアドで登録しようとしたとする。
        Bさんがうっかりこのときにactivation_tokenのあるリンクを踏むと、
        BさんのメアドのユーザーにAさんが好き勝手したアカウントのデータが結びついてしまう
  • 仮ユーザーがログインしようとした時

    • (後述)統合処理を行う
  • 仮ユーザーがパスワードリセットをしようとした時

    • メールアドレスに結びつくユーザーからreset_tokenを発行する
    • reset_token付きメールを開いて設定し、login画面へ送る(この時、sessionは一切変更しないこと)
    • 処理はsign_upと同じ
    • = (後述)統合処理を行う
  • 統合処理について

    • 基本的にはそのアプリケーションでしっかり色々考えて実装すべき部分。ここでは汎用的に考慮すべき点を考える。
    • 状態推移としては以下の2つが考えられる。
      • もともとsessionにユーザーが紐付いてない → 認証済みユーザーのログイン状態
      • sessionに仮ユーザーが結びついてる → 認証済みユーザーのログイン状態
    • sessionに結びつく仮ユーザーはこのタイミング(ログイン時)では削除などを行わないことに留意する
    • 基本的には競合時にはsession側(仮ユーザー)の優先度を下げる
    • session外への適応はユーザーにとって意図しないリソース作成を招くので注意する
      • つまり、リセットトークンやメールアドレスの所有確認のトークンを直接結びつけるのはNG
      • session外になるケース
        以下の2ケースが区別できないので、
        安全(他人のメールアドレスにリソースを予め紐付ける気味悪い現象を起こさない方)に倒す
        • PCで操作して、登録・リセットなどのトークン付きをスマホで開くケース
        • 他人のメールアドレスを誤って(or意図的に)入力してしまうケース
    • 「それ」をユーザーがしたい場合はその未ログインで仮ユーザー状態のブラウザでログインを実行してもらう
    • 実際の統合処理は、sessionに紐付いていた仮ユーザーの持つレコード(e.g. votes, contents)のuser_idをログインしたユーザーでvalidな範囲で上書きするイメージ
    • 実際には全部上書き出来ない(unique成約など)可能性もあるので、それは仮ユーザーに結びつけたままにする。物によってはユーザーに通知をしてもいいかも。
    • userのlast_activity_at的な値を観て、削除する(→session cookieの有効期限を考えて、それを過ぎている仮登録状態のユーザーレコードを削除するbatchをdailyで回すなどする)

以上です。
書き出してみたらげんなりする感じになったので、実際にサービスにこういう仕様を入れるのは嫌だなあという気持ちになりました。

Discussion