パスワードレス認証
メールアドレスと検証コードでログイン
2025.07
当書は技術設計サービス向けのサンプル設計書です。
概要
- メールアドレス検証用コード形式ログイン
 - メールアドレスがユーザーアカウントの識別子として機能する
 - 登録とログインを区別せず、初ログインの際に裏側で登録されます
 - 検証コードは10分以内入力可
 - 3回不正入力するとコード使えなくなります
 - セッション期限は特に無しだが、を定期的に再発行される
 
UIの仕様
アカウントコンポーネント
トップバーの右端に配置します。
ログイン済みの場合、アカウント名とプロフィール写真を表示し、クリックするとプロフィール画面へ移動する。
未ログインの場合、「ログイン」ボタンを表示し、クリックするとログイン画面へ移動する。
ログイン画面に移す直前に、現在のURLをsessionStorageのlogin_redirect_url
キーで保存し、ログイン成功後に復元できるようにする。
プロフィール画面
ユーザーの名前、メールアドレス、プロフィール画像を表示する。
ログアウトボタンを含む。
ログイン画面
2つのモードを持つログインフォームを表示する
メールアドレス入力モード
- メールアドレス項目
 - 主要ボタン:「検証コードを送信」
 
検証コード入力モード
- 検証コードの項目
 - 主要ボタン:「ログイン」
 - 前段階で入力したメールアドレスが保持されます(編集不可)
 
認証後の遷移
sessionStorageのlogin_redirect_urlキーを確認する。設定された場合、値をクリアし、指定されたルートへ遷移。設定されなかった場合、トップ画面へ遷移する。
DBテーブル
効率性向上のため、DBでは数値のuser_idを内部的に使用するが、UIでは非公開です。
セッション
BE側で16バイトのバッファを暗号学的に安全な疑似乱数生成器で埋め、base64でエンコードしてセッショントークンを生成する。
FE側では、localStorageに"session_token"キーでセッショントークンを保存する。
各APIリクエスト送信時に、"X-Session-Token"ヘッダーにセッショントークンを含めて提示する。
FE側で、グローバルにアクセス可能なUserProfile型の単一インスタンスがあるようにする
type UserProfile = {
    email: string
    name: string
    picture_url: string
}
    各処理の説明
注目する動作は以下の通り:
- 
ログイン画面の操作
 - 
初回ロード時のセッション状態確認
 
したがって、以下のプロセスを説明する:
- 
ログイン検証コードの発行と送信
 - 
ログイン検証コードの確認
 - 
セッショントークンの確認
 - 
セッショントークンの削除
 
ログイン検証コードの発行と送信
発生時:ユーザーがログイン画面にてメールアドレス入力し、「検証コードの送信」ボタンを押したとき。
API:
POST /api/request_login_code
Request:
{
    email: string
}
OK Response (status 200)
{
}
Error Response (status 400)
{
}
FE側の流れ
- 
メールアドレスのバリデーションは最低限の形式チェックにとどめます:
- '@'があること
 - '@'の前1文字以上あること
 - '@'の後ろの文字列に'.'があり、その前と後ろ1文字以上があること
 
 - 
メールアドレスが形式的に無効の場合、適切なエラーメッセージを表示する
- フォームのエラー表示やUXの詳細は本書の範囲外となります
 
 - 
メールアドレスが形式的に有効の場合、BE側の
request_login_codeのAPIに送信する。 - 
BE側でOKレスポンスが返却された場合、ログインフォームを検証コード入力モードに移行する。
 
BE側の流れ
- 
FE側と同様の最低限の形式チェックを実施し、無効の場合、空のエラーレスポンスを返す。
 - 
検証コードをランダム生成:
- 数字と大文字のみ使用。
 - 曖昧な文字(0 O I 1)を避ける。
 - 文字数は6文字とする
 
 - 
生成したコードを
login_codesテーブルに保存、created_atを現在時刻に設定。 - 
メール本文と件名に検証コードを明記し、「検証コード」の文言を明確に含む。
- 
メールは短く簡潔にし、ユーザーの注意を散漫にさせない。
 - 
検証コードのように見える他の要素を入れない。
 
 - 
 - 
指定されたメールアドレスに送信する。
- メール送信機構の詳細は本書の範囲外
 
 - 
FE側にOKレスポンスを返す
- (任意)メール送信処理がレスポンスをブロックしないようにする。
 
 
以下に基本ケースのシーケンス図を示す
ログイン検証コードの確認
発生時:ユーザーが検証コードを入力し、「ログイン」ボタンを押したとき。
API:
POST /api/verify_login_code
Request:
{
    email: string
    code: string
}
OK Response (status 200)
{
    session_token: string
    user_profile: UserProfile
}
Error Response (status 400)
{
}
FE側の流れ
- 
ログイン画面が「検証コード入力モード」の状態で、ユーザーが検証コードを入力し、サーバーに送信する。
 - 
コードが有効の場合、OKレスポンスがセッショントークンとプロフィール情報を含む
- 
セッショントークンをlocalStorageの
session_tokenキーに保存。 - 
グローバル
UserProfileインスタンスを更新 
 - 
 - 
不正の場合、失敗カウンターを増やす
- 3回目に達した際に、やり直しを促すエラーメッセージと、フォームを初期状態に戻すリンクを表示する
 
 
BE側の流れ:
- 
login_codesテーブルをemailでクエリし、コード一致と有効性を確認 - 
コードが一致していても、以下の条件により無効と判定される
failed_countは >= 3created_atから経過した時間は > 10分
 - 
コード不一致の場合、fail_countを1増やす
 - 
期限切れの場合、エントリ削除
 - 
エラーレスポンスは空(理由非開示)
 - 
受理した場合:
login_codesエントリ削除。user_accountsをemailでクエリ。- 存在しない場合、新規作成:
user_profilesに新IDでエントリ作成。user_accountsにemailとIDを紐付け。- 歓迎メール送信。
 
 - 存在する場合、
user_profilesからロード。 - 新セッショントークン生成、
sessionsに保存。 - OKレスポンスにトークンとプロフィールを含む。
 
 
以下に各種ケースのシーケンス図を示す。
セッショントークンの確認
発生時:ページ初回ロード時の初期化処理。
API:
POST /api/verify_session_token
Request:
{
    session_token: string
}
OK Response (status 200)
{
    session_token: string
    user_profile: UserProfile
}
Error Response (status 400)
{
}
FE側の流れ
localStorageから"session_token"を読み込む。- 存在する場合、
verify_session_tokenAPIで確認。 - OKの場合、レスポンスでトークン更新、グローバル
UserProfile設定。 - エラーの場合、トークン削除、
UserProfileをnullに。 
BE側の流れ
sessionsテーブルでトークンクエリ。- 結果なしの場合、エラー返す。
 
- 作成から24時間超の場合、更新:
- 新トークン生成。
 - 旧セッション削除。
 - 新セッション作成、
created_atを設定。 
 user_profilesからプロフィールを読み取り。- データなしの場合、セッション削除、エラー返す。
 
- トークンとプロフィールでOKレスポンス作成。
 
以下に各種ケースのシーケンス図を示す。
セッショントークンの削除
発生時:ログアウトボタンクリック。
API:
POST /api/delete_session_token
Request:
{
    session_token: string
}
OK Response (status 200)
{
}
FE側の流れ
localStorageからトークンを変数に読み込み、削除。- BE側にトークン送信して削除依頼。
 - レスポンス待たず、トップ画面へ遷移し、フルリロードする(データ残存防止)。
 
BE側の流れ
- DBでトークン一致のセッション削除。
 - 空のOKレスポンス返す。
 
エラー対応不要、FE側はレスポンス確認しない。
まとめ
パスワードレスログインの実装支援のために、以下を洗い出しました:
- データモデル
 - 処理の流れ
 - APIの定義