パスワードレス認証
メールアドレスと検証コードでログイン
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_token
APIで確認。 - 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の定義