認証フォーム 設計書
概要
サインアップ・サインインフォームの実装仕様。パスワード表示トグル・強度メーター・バリデーション表示の空間共有を含む。
コンポーネント構成
パスワード表示トグル
PasswordInput は originui の input-group をベースとした複合コンポーネント。
- デフォルト:
type="password" - トグルボタンで
type="text"に切り替え - アイコン: Phosphor Icons
PhEye/PhEyeSlash focus/blurイベントを emit し、親フォームがpasswordFocusedを管理する
パスワード強度判定
アーキテクチャ
zxcvbn-ts は非圧縮約 4.7MB のためクライアントバンドルに乗せない。 スコアリングは Elysia SSR サーバーで行い、クライアントは API 経由で結果を取得する。
クライアント
└─ usePasswordStrength(watchDebounced 300ms)
└─ POST /internal/password-strength
└─ Elysia サーバー(zxcvbn-ts)
└─ { strength: 'low' | 'medium' | 'high' | '' }
/internal/ プレフィックスにより Vite dev proxy(/api/* → Rust backend)と衝突しない。
dev / prod 両環境で Elysia が直接処理する(elysia.ts 経由)。
エンドポイント
POST /internal/password-strength
リクエスト: { password: string }
レスポンス: { strength: '' | 'low' | 'medium' | 'high' }
スコアマッピング
辞書設定
zxcvbnOptions.setOptions({
dictionary: {
...commonPackage.dictionary,
jaPasswords: jaPackage.dictionary.commonWords,
},
graphs: commonPackage.adjacencyGraphs,
});
日本語ローマ字パスワード(sakura, hanako 等)の低評価のため jaPasswords を追加。
英語辞書よりペナルティを低く抑えるため commonWords サブセットを使用。
レースコンディション対策
高速入力時に古いレスポンスが後から届いて上書きするのを防ぐため、seq ID で管理する。
let seq = 0;
// 空文字リセット時: 進行中リクエストを無効化
seq++;
// fetch 前
const id = ++seq;
// 受信時: stale なら破棄
if (!response.ok || id !== seq) return;
SignUpForm 表示ロジック
TanStack Form + ArkType を使用。
hasSubmitted フラグで送信後は即時バリデーションに切り替える。
ユーザー名フィールド下部の空間共有
min-h-[1.25rem] コンテナに以下を排他表示する。
パスワードフィールド下部の空間共有
min-h-[1.5rem] コンテナに以下を排他表示する。
フォーカス中はエラーを強度バーで上書きする(入力継続中のユーザーに強度フィードバックを優先)。 0文字のとき常時ヒントを表示することで、フォーカス前から要件をユーザーに伝える。
PasswordStrengthBar
- 3セグメントのカラーバー(赤 / 黄 / 緑)
v-if="strength"により空文字のとき自身は何も描画しない- ラベル: 弱い / 普通 / 強い
role="meter"+aria-valuenow/aria-valuemin/aria-valuemaxで WAI-ARIA 対応
今後の課題(次 PR 予定)
POST /v1/auth/register//v1/auth/loginAPI 連携- 登録失敗(409: ユーザー名・メール重複)をバリデーション空間に表示
- 2FA(login 200 レスポンス)対応: 現状 TODO コメントのみ