Skip to content

feat(front): auth forms - #134

Merged
yupix merged 15 commits into
mainfrom
feat/front-auth-forms
Jun 16, 2026
Merged

feat(front): auth forms#134
yupix merged 15 commits into
mainfrom
feat/front-auth-forms

Conversation

@sousuke0422

@sousuke0422 sousuke0422 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

#8 のPhase 1

パスワード辞書をバンドルしないためにelysiaで処理

2faは後発

blocking by #114 (#113) done

計画

  1. これ
  2. api連携
  3. 2fa?
    3.5. api連携?
  4. passkey?
    4.5. api連携?

Summary by CodeRabbit

リリースノート

  • 新機能
    • パスワードの表示/非表示トグルと強度バー(弱い/普通/強い)を追加
    • パスワード入力に応じて強度を自動表示(アクセシビリティ対応)
    • 入力グループ/テキストエリアの再利用コンポーネントを追加
    • サインイン・サインアップフォームを新しいフォーム管理に刷新
  • 改善(不具合修正)
    • 強度判定の競合を抑え、通信失敗時は表示を維持
    • フィールド別のエラー表示を改善
  • テスト
    • 強度判定とエラーメッセージ変換のテストを追加
  • ドキュメント
    • 認証フォーム設計書を追加

@sousuke0422 sousuke0422 self-assigned this Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

認証フォームに TanStack Form + ArkType によるバリデーションを導入し、zxcvbn-ts を Elysia SSR 側に集約した /internal/password-strength エンドポイントを新設した。usePasswordStrength コンポーザブル、PasswordInputPasswordStrengthBar、InputGroup 系 UI プリミティブを追加し、SignInFormSignUpForm を新しいフォーム管理に移行した。

Changes

認証フォーム刷新とパスワード強度判定

Layer / File(s) Summary
依存パッケージ追加と SSR ビルド設定
apps/frontend/package.json, apps/frontend/vite.config.ts
@tanstack/vue-form@zxcvbn-ts/* を dependencies に追加。SSR ビルドで @zxcvbn-ts/* パッケージを外部化しない設定を追加。
SSR 側 /internal/password-strength エンドポイント
apps/frontend/server/elysia.ts
zxcvbn-ts の import、PasswordStrength 型定義、辞書・隣接グラフの初期化、scoreToStrength マッピング、POST /internal/password-strength ルートを実装。
InputGroup UI プリミティブと Textarea コンポーネント
apps/frontend/src/components/originui/input-group/*, apps/frontend/src/components/ui/textarea/*
InputGroup / Addon / Button / Input / Text / Textarea の Vue SFC、cva バリアント定義、Textarea コンポーネントを追加。
usePasswordStrength コンポーザブルと包括的なテスト
apps/frontend/src/composables/usePasswordStrength.ts, apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts
300ms デバウンス+seq 競合制御で強度を取得するコンポーザブル実装。デバウンスタイミング、stale レスポンス破棄、エラー時挙動、レース条件対策を網羅するテストスイート。
PasswordInput と PasswordStrengthBar コンポーネント
apps/frontend/src/components/auth/PasswordInput.vue, apps/frontend/src/components/auth/PasswordStrengthBar.vue
表示/非表示トグル付きパスワード入力と 3 段階カラー強度バーを実装。WAI-ARIA 属性対応、blur/focus イベント対応。
認証バリデーション補助関数とテスト
apps/frontend/src/lib/auth-validation.ts, apps/frontend/src/lib/__tests__/auth-validation.test.ts
arktype エラーメッセージを日本語に変換する arkMessage ユーティリティと、変換ロジックを検証するテストスイートを追加。
SignInForm の TanStack Form 移行
apps/frontend/src/components/auth/SignInForm.vue
useForm + arktype によるバリデーション、arkMessage による日本語エラー文言変換、form.Field / form.Subscribe による UI 制御、送信処理スタブ化。
SignUpForm の TanStack Form 移行と強度判定統合
apps/frontend/src/components/auth/SignUpForm.vue
useForm + arktype スキーマ、usePasswordStrength + PasswordStrengthBar による強度表示、フィールド単位のエラー表示、フォーカス状態管理、送信処理 TODO スタブ化。
認証フォーム設計ドキュメント
docs/frontend/auth-forms.md
コンポーネント一覧、PasswordInput 仕様、/internal/password-strength アーキテクチャ、zxcvbn スコア→strength マッピング、seq 競合対策、SignUpForm バリデーション・表示ロジック、次 PR 課題。

Sequence Diagram(s)

sequenceDiagram
  participant ユーザー
  participant SignUpForm
  participant usePasswordStrength
  participant ElysiaSSR as Elysia SSR
  
  rect rgba(100, 150, 255, 0.5)
    note over ユーザー,SignUpForm: パスワード入力フェーズ
    ユーザー->>SignUpForm: パスワード入力
    SignUpForm->>usePasswordStrength: password ref 更新
    usePasswordStrength->>usePasswordStrength: 300ms デバウンス待機<br/>(seq++)
  end
  
  rect rgba(100, 200, 150, 0.5)
    note over usePasswordStrength,ElysiaSSR: 強度取得フェーズ
    usePasswordStrength->>ElysiaSSR: POST /internal/password-strength<br/>{password}
    ElysiaSSR->>ElysiaSSR: zxcvbn(password)
    ElysiaSSR->>ElysiaSSR: scoreToStrength(score)
    ElysiaSSR-->>usePasswordStrength: {strength: "low"|"medium"|"high"}
    usePasswordStrength->>usePasswordStrength: seq チェック<br/>(stale は破棄)
    usePasswordStrength-->>SignUpForm: strength ref 更新
  end
  
  rect rgba(255, 180, 100, 0.5)
    note over SignUpForm,ユーザー: UI 更新フェーズ
    SignUpForm->>ユーザー: PasswordStrengthBar<br/>を強度に応じて描画
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60分

Poem

🐇✨ うさぎが跳ねる フォームの野原
zxcvbn で 強さを測り
stale な返事は ポイと捨てて
seq で守る 最新の答え
タンスタック乗って 形が決まる 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトル「feat(front): auth forms」は、このPRの主要な変更であるAuthenticationフォームコンポーネントの導入を簡潔かつ明確に表現しており、ファイル変更の概要(パスワード入力、強度表示、フォーム管理など)を適切に要約しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/front-auth-forms

Comment @coderabbitai help to get the list of available commands and usage tips.

@sousuke0422 sousuke0422 added kind/新機能 New feature or request priority/high labels Jun 15, 2026
@sousuke0422
sousuke0422 force-pushed the feat/front-auth-forms branch from 55c77c1 to 6ad9d2a Compare June 15, 2026 13:34
@sousuke0422
sousuke0422 marked this pull request as ready for review June 15, 2026 13:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/frontend/server/elysia.ts`:
- Around line 40-42: The password field in the `/internal/password-strength`
endpoint's request body schema at lines 40-42 uses t.String() without any length
constraints, which allows arbitrarily long inputs to be processed by the
expensive zxcvbn() password strength calculation, creating a DoS vulnerability.
Add a maxLength constraint to the password field definition in the t.Object body
schema to enforce a reasonable maximum length and reject oversized inputs early
at the validation layer.

In `@apps/frontend/src/components/auth/SignInForm.vue`:
- Line 26: Plain text logging of sensitive authentication data (passwords) is
present in two sign-in/sign-up form components, creating a data leakage risk in
both development and production environments. In
apps/frontend/src/components/auth/SignInForm.vue at line 26, remove the
console.log('signin stub', value) statement since value contains the password,
and replace it with an audit log that excludes sensitive information if logging
is needed. Similarly, in apps/frontend/src/components/auth/SignUpForm.vue at
line 37, remove the console.log('signup stub', value) statement and replace it
with a safe audit log that masks or excludes the sensitive authentication data.
- Around line 24-27: The authentication form submissions are stubbed with
console.log and TODO comments instead of implementing actual API functionality.
In apps/frontend/src/components/auth/SignInForm.vue at the onSubmit function
(lines 24-27), replace the stub implementation with a POST request to the
/v1/auth/login endpoint, add error handling to display validation or error
messages to the user, and navigate to the appropriate page on successful
authentication. Apply the same pattern to
apps/frontend/src/components/auth/SignUpForm.vue at lines 34-38, implementing
the sign-up API call to /v1/auth/register with corresponding error handling and
success-path navigation.

In `@apps/frontend/src/components/originui/input-group/InputGroupAddon.vue`:
- Around line 23-24: The querySelector on line 24 in InputGroupAddonVue is
hardcoded to find only 'input' elements, which causes focus to not be
transferred when using InputGroupTextarea. Replace the selector 'input' with
'[data-slot="input-group-control"]' to target the appropriate control element
regardless of whether it is an input or textarea, ensuring focus is properly set
for both component types.

In `@apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts`:
- Around line 107-117: Add a new test case after the existing 'API がエラーを返したとき
strength を更新しない' test to cover the scenario where fetch rejects with an
exception (network failure), rather than just returning an HTTP error response.
Mock fetch to throw/reject an error instead of returning a failed response, then
verify that strength.value remains empty after the async operation completes,
ensuring the composable handles uncaught fetch exceptions gracefully without
regressions.

In `@apps/frontend/src/composables/usePasswordStrength.ts`:
- Around line 33-43: The fetch call and response.json() operation in the
composable lack error handling, causing unhandled exceptions when the network
request or JSON parsing fails. Wrap the entire fetch and JSON parsing logic
(starting from the fetch call for '/internal/password-strength' through the
response.json() call) in a try/catch block. Maintain the existing condition that
checks response.ok and verifies that id matches seq to ensure only the latest
request updates the state, and handle any caught exceptions gracefully by simply
returning early to prevent state updates on error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 317c0788-697b-42f4-81df-b40dc3af7453

📥 Commits

Reviewing files that changed from the base of the PR and between 633e56b and 6ad9d2a.

⛔ Files ignored due to path filters (1)
  • apps/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • apps/frontend/package.json
  • apps/frontend/server/elysia.ts
  • apps/frontend/src/components/auth/PasswordInput.vue
  • apps/frontend/src/components/auth/PasswordStrengthBar.vue
  • apps/frontend/src/components/auth/SignInForm.vue
  • apps/frontend/src/components/auth/SignUpForm.vue
  • apps/frontend/src/components/originui/input-group/InputGroup.vue
  • apps/frontend/src/components/originui/input-group/InputGroupAddon.vue
  • apps/frontend/src/components/originui/input-group/InputGroupButton.vue
  • apps/frontend/src/components/originui/input-group/InputGroupInput.vue
  • apps/frontend/src/components/originui/input-group/InputGroupText.vue
  • apps/frontend/src/components/originui/input-group/InputGroupTextarea.vue
  • apps/frontend/src/components/originui/input-group/index.ts
  • apps/frontend/src/components/ui/textarea/Textarea.vue
  • apps/frontend/src/components/ui/textarea/index.ts
  • apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts
  • apps/frontend/src/composables/usePasswordStrength.ts
  • apps/frontend/vite.config.ts
  • docs/frontend/auth-forms.md

Comment thread apps/frontend/server/elysia.ts
Comment thread apps/frontend/src/components/auth/SignInForm.vue
Comment thread apps/frontend/src/components/auth/SignInForm.vue
Comment thread apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts
Comment thread apps/frontend/src/composables/usePasswordStrength.ts Outdated
@sousuke0422
sousuke0422 force-pushed the feat/front-auth-forms branch from a942dae to 5876f80 Compare June 15, 2026 17:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
apps/frontend/src/components/originui/input-group/InputGroupAddon.vue (1)

23-24: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

InputGroupTextarea 構成でフォーカス移譲が機能しません。

Line 24 が input 固定検索のため、InputGroupTextarea 利用時にアドオンクリックでフォーカスできません。data-slot="input-group-control" を対象にしてください。

修正案
-  if (currentTarget && currentTarget?.parentElement) {
-    currentTarget.parentElement?.querySelector('input')?.focus();
-  }
+  if (currentTarget?.parentElement) {
+    currentTarget.parentElement
+      .querySelector<HTMLElement>('[data-slot="input-group-control"]')
+      ?.focus();
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/src/components/originui/input-group/InputGroupAddon.vue` around
lines 23 - 24, The querySelector on line 24 in InputGroupAddon.vue is hardcoded
to search for 'input' elements only, which causes focus delegation to fail when
using InputGroupTextarea (which uses textarea instead). Replace the
querySelector('input') call with
querySelector('[data-slot="input-group-control"]') to target the appropriate
control element by its data attribute, which will work correctly for both input
and textarea elements in the component tree.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/frontend/src/components/auth/SignUpForm.vue`:
- Line 26: The `hasSubmitted` ref variable is declared but never used in the
template or script logic. Remove the `const hasSubmitted = ref(false);`
declaration from the component and also remove any assignment to `hasSubmitted`
(such as `hasSubmitted.value = true;`) in the `onSubmit` function if present. If
you plan to use this for future functionality like displaying success messages
after form submission, keep it; otherwise, delete it entirely to keep the code
clean.

In `@apps/frontend/src/composables/usePasswordStrength.ts`:
- Around line 29-43: The async watch callback in usePasswordStrength composable
currently sends POST requests to /internal/password-strength even when the
password exceeds the server constraint of 256 characters, causing unnecessary
requests and stale strength display values. Add a client-side validation check
in the callback body (after the empty value check but before the fetch call) to
verify that the password length does not exceed 256 characters; if it does
exceed 256 characters, reset strength.value to an empty string and return early
to prevent the unnecessary POST request. Consider adding a test case to verify
this behavior prevents regressions.

In `@docs/frontend/auth-forms.md`:
- Around line 50-51: The documentation at lines 50-51 references `+server.ts`
which does not match the actual implementation. The actual SSR entry point in
the codebase is `apps/frontend/server/elysia.ts`. Replace the `+server.ts`
notation in the documentation with the correct implementation filename
`elysia.ts` (or the full path if needed for clarity) to accurately reflect how
the Elysia server is configured and avoid confusing readers who might expect a
different framework.
- Around line 42-48: The fenced code block in the documentation is missing a
language specification, which triggers lint warnings. Add the language
identifier `text` to the opening backticks of the code fence (change ``` to
```text) to make the intent clear and comply with linting standards.

---

Duplicate comments:
In `@apps/frontend/src/components/originui/input-group/InputGroupAddon.vue`:
- Around line 23-24: The querySelector on line 24 in InputGroupAddon.vue is
hardcoded to search for 'input' elements only, which causes focus delegation to
fail when using InputGroupTextarea (which uses textarea instead). Replace the
querySelector('input') call with
querySelector('[data-slot="input-group-control"]') to target the appropriate
control element by its data attribute, which will work correctly for both input
and textarea elements in the component tree.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eb85c3a2-c50c-4581-b086-821b092aa96c

📥 Commits

Reviewing files that changed from the base of the PR and between a942dae and 5876f80.

⛔ Files ignored due to path filters (1)
  • apps/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • apps/frontend/package.json
  • apps/frontend/server/elysia.ts
  • apps/frontend/src/components/auth/PasswordInput.vue
  • apps/frontend/src/components/auth/PasswordStrengthBar.vue
  • apps/frontend/src/components/auth/SignInForm.vue
  • apps/frontend/src/components/auth/SignUpForm.vue
  • apps/frontend/src/components/originui/input-group/InputGroup.vue
  • apps/frontend/src/components/originui/input-group/InputGroupAddon.vue
  • apps/frontend/src/components/originui/input-group/InputGroupButton.vue
  • apps/frontend/src/components/originui/input-group/InputGroupInput.vue
  • apps/frontend/src/components/originui/input-group/InputGroupText.vue
  • apps/frontend/src/components/originui/input-group/InputGroupTextarea.vue
  • apps/frontend/src/components/originui/input-group/index.ts
  • apps/frontend/src/components/ui/textarea/Textarea.vue
  • apps/frontend/src/components/ui/textarea/index.ts
  • apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts
  • apps/frontend/src/composables/usePasswordStrength.ts
  • apps/frontend/vite.config.ts
  • docs/frontend/auth-forms.md

Comment thread apps/frontend/src/components/auth/SignUpForm.vue Outdated
Comment thread apps/frontend/src/composables/usePasswordStrength.ts
Comment thread docs/frontend/auth-forms.md Outdated
Comment thread docs/frontend/auth-forms.md Outdated
sousuke0422 added a commit that referenced this pull request Jun 15, 2026
- SignUpForm: add TODO comment on hasSubmitted (declared but not yet read in template)
- docs/auth-forms: add language spec to fenced code block (text)
- docs/auth-forms: fix incorrect +server.ts reference → elysia.ts

Assisted-by: multi-agent-shogun-aki-tweak

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend/src/components/auth/SignInForm.vue (1)

66-70: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

PasswordInputname 受け渡しがフォーム側で未適用です。

PasswordInput.vuename prop を追加していますが、利用側2箇所で未指定のため、実入力要素に name が設定されず、パスワードマネージャー/自動入力の一致率が低下します。Line 66Line 100 付近で :name="field.name" を渡してください。

  • apps/frontend/src/components/auth/SignInForm.vue#L66-L70: <PasswordInput ... :name="field.name" /> を追加してサインイン入力の name を明示してください。
  • apps/frontend/src/components/auth/SignUpForm.vue#L100-L104: <PasswordInput ... :name="field.name" /> を追加して登録入力の name を明示してください。
🔧 修正例
--- a/apps/frontend/src/components/auth/SignInForm.vue
+++ b/apps/frontend/src/components/auth/SignInForm.vue
@@
                   <PasswordInput
                     :id="field.name"
+                    :name="field.name"
                     autocomplete="current-password"
                     :model-value="field.state.value"
                     `@update`:model-value="field.handleChange"
                     `@blur`="field.handleBlur"
                   />
--- a/apps/frontend/src/components/auth/SignUpForm.vue
+++ b/apps/frontend/src/components/auth/SignUpForm.vue
@@
                   <PasswordInput
                     :id="field.name"
+                    :name="field.name"
                     autocomplete="new-password"
                     :model-value="field.state.value"
                     `@update`:model-value="
                       (v: string) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/src/components/auth/SignInForm.vue` around lines 66 - 70, The
PasswordInput component now accepts a name prop, but it is not being passed in
the two locations where the component is used, preventing the actual input
element from having a name attribute set, which breaks password manager and
autofill functionality. In apps/frontend/src/components/auth/SignInForm.vue at
lines 66-70, add the `:name="field.name"` binding to the PasswordInput
component. Additionally, in apps/frontend/src/components/auth/SignUpForm.vue at
lines 100-104, add the same `:name="field.name"` binding to the PasswordInput
component to ensure both sign-in and sign-up forms properly expose the name
attribute to autofill features.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/frontend/src/components/auth/SignInForm.vue`:
- Around line 66-70: The PasswordInput component now accepts a name prop, but it
is not being passed in the two locations where the component is used, preventing
the actual input element from having a name attribute set, which breaks password
manager and autofill functionality. In
apps/frontend/src/components/auth/SignInForm.vue at lines 66-70, add the
`:name="field.name"` binding to the PasswordInput component. Additionally, in
apps/frontend/src/components/auth/SignUpForm.vue at lines 100-104, add the same
`:name="field.name"` binding to the PasswordInput component to ensure both
sign-in and sign-up forms properly expose the name attribute to autofill
features.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 997506d8-31dd-4531-8825-44fea0de1e17

📥 Commits

Reviewing files that changed from the base of the PR and between 803727d and d312e54.

📒 Files selected for processing (5)
  • apps/frontend/src/components/auth/PasswordInput.vue
  • apps/frontend/src/components/auth/SignInForm.vue
  • apps/frontend/src/components/auth/SignUpForm.vue
  • apps/frontend/src/lib/__tests__/auth-validation.test.ts
  • apps/frontend/src/lib/auth-validation.ts

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #138

coderabbitai Bot added a commit that referenced this pull request Jun 15, 2026
Docstrings generation was requested by @sousuke0422.

* #134 (comment)

The following files were modified:

* `apps/frontend/server/elysia.ts`
* `apps/frontend/src/composables/usePasswordStrength.ts`
* `apps/frontend/src/lib/auth-validation.ts`
Assisted-by: multi-agent-shogun-aki-tweak
Assisted-by: multi-agent-shogun-aki-tweak
フォーカス前からユーザー名・パスワードの要件を常時表示する。
ユーザー名フィールドはエラー非表示時に「3文字以上」、
パスワードフィールドはエラー非表示かつ強度バー非表示時に「8文字以上」を表示。

Assisted-by: multi-agent-shogun-aki-tweak
…rejection test

elysia.ts: t.String({ maxLength: 256 }) で過大入力を早期拒否(DoS耐性)
usePasswordStrength.test.ts: fetch がネットワークエラーで reject した場合のテストを追加

Assisted-by: multi-agent-shogun-aki-tweak
…rors

未捕捉例外を防ぐため fetch と response.json() を try/catch で囲む。
エラー時は strength を更新せず前の値を維持する。

Assisted-by: multi-agent-shogun-aki-tweak
…andling

ネットワークエラーで reject するケースと、nginx 等が HTML を返す
ケース(JSON パース失敗)で strength が更新されないことを確認する。

Assisted-by: multi-agent-shogun-aki-tweak
- SignUpForm: add TODO comment on hasSubmitted (declared but not yet read in template)
- docs/auth-forms: add language spec to fenced code block (text)
- docs/auth-forms: fix incorrect +server.ts reference → elysia.ts

Assisted-by: multi-agent-shogun-aki-tweak
…Strength

Deferred until backend enforces a concrete limit.

Assisted-by: multi-agent-shogun-aki-tweak
- Extract shared arkMessage() to src/lib/auth-validation.ts
- Remove duplicate definitions from SignInForm and SignUpForm
- Add name prop to PasswordInput for password manager support

Assisted-by: multi-agent-shogun-aki-tweak
Assisted-by: multi-agent-shogun-aki-tweak
Assisted-by: multi-agent-shogun-aki-tweak
Assisted-by: multi-agent-shogun-aki-tweak
@sousuke0422
sousuke0422 force-pushed the feat/front-auth-forms branch from bc99450 to c9d461d Compare June 16, 2026 04:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
apps/frontend/src/composables/usePasswordStrength.ts (1)

29-44: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

サーバ契約の maxLength: 256 をクライアント側でも先に判定してください。

Line 31 の TODO が未実装のため、256 文字超でも POST が走ります。サーバ拒否時に strength が直前値のまま残り、表示が実入力と不整合になります。value.length > 256 で即時リセット+早期 return を入れてください(同ケースの回帰テスト追加も必要です)。

差分案
   watchDebounced(
     password,
     async (value) => {
       if (!value) return;
-      // TODO: add client-side maxLength guard once backend enforces a limit (e.g. 256)
-
       const id = ++seq;
+      if (value.length > 256) {
+        strength.value = '';
+        return;
+      }
+
       try {
         const response = await fetch('/internal/password-strength', {
           method: 'POST',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/src/composables/usePasswordStrength.ts` around lines 29 - 44,
The TODO comment on line 31 for client-side maxLength validation is not
implemented, allowing POST requests to be made for passwords exceeding 256
characters. When the server rejects these requests, the strength value remains
unchanged, causing a display mismatch with the actual input. Implement the
maxLength guard by adding a check after the initial `if (!value) return;`
statement to validate that value.length does not exceed 256 characters; if it
does exceed this limit, reset the strength value to a default state and return
early before attempting the fetch request. Additionally, add regression tests to
verify that strength is properly reset when password length exceeds 256
characters.
apps/frontend/src/components/auth/SignInForm.vue (2)

19-22: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

送信処理が両フォームでスタブ化され、認証フローが停止しています。

根因は、onSubmit が TODO のままで実API連携を持たないことです。

  • apps/frontend/src/components/auth/SignInForm.vue#L19-L22: /v1/auth/login 呼び出し、失敗時表示、成功時遷移を実装してください。
  • apps/frontend/src/components/auth/SignUpForm.vue#L28-L32: /v1/auth/register 呼び出し、失敗時表示、成功時遷移を実装してください。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/src/components/auth/SignInForm.vue` around lines 19 - 22, The
authentication flow is incomplete because both form submission handlers are
stubbed with TODO comments and console.log statements. You must implement the
onSubmit handlers in both files to complete the authentication flow. In
apps/frontend/src/components/auth/SignInForm.vue (lines 19-22), replace the
console.log stub in the onSubmit handler with an actual API call to POST
/v1/auth/login using the form value, handle errors by displaying them to the
user, and on success navigate to the authenticated user's dashboard or home
page. In apps/frontend/src/components/auth/SignUpForm.vue (lines 28-32),
implement the same pattern but call POST /v1/auth/register instead, with failure
error display and success navigation to complete the sign-up flow.

21-21: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

機微情報(パスワード)が両フォームで平文ログ出力されています。

根因は、value 全体を console.log していることです。

  • apps/frontend/src/components/auth/SignInForm.vue#L21-L21: console.log('signin stub', value) を削除し、必要なら機微情報を除外した監査ログへ置換してください。
  • apps/frontend/src/components/auth/SignUpForm.vue#L31-L31: console.log('signup stub', value) を削除し、同様に機微情報を除外したログへ置換してください。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/src/components/auth/SignInForm.vue` at line 21, Sensitive
information (passwords) from the form value object are being logged in plain
text in both authentication forms. In
apps/frontend/src/components/auth/SignInForm.vue at lines 21-21, remove the
console.log statement that logs the entire value object. In
apps/frontend/src/components/auth/SignUpForm.vue at lines 31-31, similarly
remove the console.log statement that logs the entire value object. If audit
logging is needed, replace these statements with logs that explicitly exclude
sensitive fields like passwords and only log non-sensitive metadata or user
identification information.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@apps/frontend/src/components/auth/SignInForm.vue`:
- Around line 19-22: The authentication flow is incomplete because both form
submission handlers are stubbed with TODO comments and console.log statements.
You must implement the onSubmit handlers in both files to complete the
authentication flow. In apps/frontend/src/components/auth/SignInForm.vue (lines
19-22), replace the console.log stub in the onSubmit handler with an actual API
call to POST /v1/auth/login using the form value, handle errors by displaying
them to the user, and on success navigate to the authenticated user's dashboard
or home page. In apps/frontend/src/components/auth/SignUpForm.vue (lines 28-32),
implement the same pattern but call POST /v1/auth/register instead, with failure
error display and success navigation to complete the sign-up flow.
- Line 21: Sensitive information (passwords) from the form value object are
being logged in plain text in both authentication forms. In
apps/frontend/src/components/auth/SignInForm.vue at lines 21-21, remove the
console.log statement that logs the entire value object. In
apps/frontend/src/components/auth/SignUpForm.vue at lines 31-31, similarly
remove the console.log statement that logs the entire value object. If audit
logging is needed, replace these statements with logs that explicitly exclude
sensitive fields like passwords and only log non-sensitive metadata or user
identification information.

In `@apps/frontend/src/composables/usePasswordStrength.ts`:
- Around line 29-44: The TODO comment on line 31 for client-side maxLength
validation is not implemented, allowing POST requests to be made for passwords
exceeding 256 characters. When the server rejects these requests, the strength
value remains unchanged, causing a display mismatch with the actual input.
Implement the maxLength guard by adding a check after the initial `if (!value)
return;` statement to validate that value.length does not exceed 256 characters;
if it does exceed this limit, reset the strength value to a default state and
return early before attempting the fetch request. Additionally, add regression
tests to verify that strength is properly reset when password length exceeds 256
characters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e53a9199-3f33-49fc-a6d8-04318a6ea779

📥 Commits

Reviewing files that changed from the base of the PR and between 803727d and c9d461d.

⛔ Files ignored due to path filters (1)
  • apps/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • apps/frontend/package.json
  • apps/frontend/server/elysia.ts
  • apps/frontend/src/components/auth/PasswordInput.vue
  • apps/frontend/src/components/auth/PasswordStrengthBar.vue
  • apps/frontend/src/components/auth/SignInForm.vue
  • apps/frontend/src/components/auth/SignUpForm.vue
  • apps/frontend/src/components/originui/input-group/InputGroup.vue
  • apps/frontend/src/components/originui/input-group/InputGroupAddon.vue
  • apps/frontend/src/components/originui/input-group/InputGroupButton.vue
  • apps/frontend/src/components/originui/input-group/InputGroupInput.vue
  • apps/frontend/src/components/originui/input-group/InputGroupText.vue
  • apps/frontend/src/components/originui/input-group/InputGroupTextarea.vue
  • apps/frontend/src/components/originui/input-group/index.ts
  • apps/frontend/src/components/ui/textarea/Textarea.vue
  • apps/frontend/src/components/ui/textarea/index.ts
  • apps/frontend/src/composables/__tests__/usePasswordStrength.test.ts
  • apps/frontend/src/composables/usePasswordStrength.ts
  • apps/frontend/src/lib/__tests__/auth-validation.test.ts
  • apps/frontend/src/lib/auth-validation.ts
  • apps/frontend/vite.config.ts
  • docs/frontend/auth-forms.md

@yupix

yupix commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

/opencode explain このPRを説明して

@opencode-agent

Copy link
Copy Markdown
Contributor

PR概要

認証フォーム(サインイン/サインアップ)の刷新PRです。

主な変更

技術スタック移行

  • フォーム管理: TanStack Form 導入
  • バリデーション: ArkType 採用
  • パスワード強度: zxcvbn-ts をSSR側(Elysia)に集約

新コンポーネント

  • PasswordInput - 表示/非表示トグル付き
  • PasswordStrengthBar - 弱い/普通/強い の3段階表示
  • InputGroup 系UIプリミティブ

アーキテクチャ

  • /internal/password-strength エンドポイント新設
  • クライアント側に辞書バンドルせずSSRで処理(セキュリティ対策)
  • 300msデバウンス + seq競合制御

未実装(次のPR)

  • API連携(送信処理はTODOスタブ)
  • 2FA
  • Passkey

CodeRabbitの指摘事項

  • console.logにパスワードが含まれる問題
  • fetch例外処理の未実装
  • PasswordInputのname属性未渡し
  • その他軽微な修正が提案されています

New%20session%20-%202026-06-16T16%3A47%3A01.766Z
opencode session  |  github run

@yupix

yupix commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

/opencode review このPRをレビューして

@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: feat(front): auth forms

全体的によく整理されたPRです。設計書も含めて丁寧に作られています。以下に指摘事項をまとめます。


既に修正済みの項目

CodeRabbitの指摘の多くは既に対応済みです:

  • maxLength: 256 制約 (elysia.ts:41)
  • console.log のパスワード削除 (SignInForm.vue:21, SignUpForm.vue:31)
  • try/catch による例外処理 (usePasswordStrength.ts:34-47)
  • PasswordInput:name 渡し (SignInForm.vue:68, SignUpForm.vue:102)
  • コードフェンスの言語指定 (auth-forms.md:42)

未解決の問題

1. usePasswordStrength のmaxLengthガードが未実装usePasswordStrength.ts:31

// TODO: add client-side maxLength guard once backend enforces a limit (e.g. 256)

サーバーは maxLength: 256 で弾きますが、クライアント側で256文字超のパスワードをPOSTすると400エラーになります。catch で握っているのでクラッシュしませんが、不要なリクエストが走り、前回のstrength値が残留します。

if (value.length > 256) {
  strength.value = '';
  return;
}

テストも追加すべきです。

2. hasSubmitted が未使用SignUpForm.vue:20

const hasSubmitted = ref(false); // TODO: use to display success message after registration

onSubmittrue にしていますが、テンプレート・スクリプトどちらでも参照されていません。PR descriptionに「次PR予定」とあるので残してあるのでしょうが、不要であれば削除、残すならTODOコメントを明確化した方が良いです。

3. arkMessage の脆弱性auth-validation.ts:7-11

export function arkMessage(msg: string): string {
  if (msg.includes('at least length 3')) return '3文字以上で入力してください。';
  if (msg.includes('email address')) return 'メールアドレスの形式が正しくありません。';
  if (msg.includes('at least length 8')) return '8文字以上で入力してください。';
  return msg;
}
  • includes による部分マッチは、arktypeのエラーメッセージがバージョンアップで変わると壊れやすい
  • マッチしなかった場合に英語の原文がそのまま表示され、日本語UIの中で違和感がある
  • テストは20行のみで、実際のarktype出力との整合テストが薄い

将来的にはarktypeのエラーツリーを直接カスタマイズする方が堅牢です。

4. auth-forms.md の記述が実装と不一致auth-forms.md:113

hasSubmitted フラグで送信後は即時バリデーションに切り替える。

実装では hasSubmitted はバリデーションタイミングに影響していません(validators: { onSubmit: schema} のみ)。ドキュメントと実装の乖離があります。


軽微な指摘

5. InputGroupAddon のフォーカス移譲InputGroupAddon.vue:24

CodeRabbitが指摘済みですが、querySelector('input')querySelector('[data-slot="input-group-control"]') への修正が未対応です。InputGroupTextarea 使用時にフォーカスが当たりません。

6. scoreToStrength の閾値elysia.ts:20-24

function scoreToStrength(score: number): PasswordStrength {
  if (score <= 1) return 'low';
  if (score <= 3) return 'medium';
  return 'high';
}

zxcvbnのscoreは0-4で、設計書のマッピングと一致しています。ただし score <= 1score === 0 || score === 1 のみをカバーするので、scoreが小数になるケース(zxcvbnの仕様上ありえないが)を考慮するなら Math.floor が安全です。

7. サインインフォームのバリデーションタイミングSignInForm.vue:38,62

:validators="{ onBlur: type('string.email') }"
:validators="{ onBlur: type('string >= 8') }"

SignUpFormと異なり、SignInFormには onSubmit スキーマのみで onBlur の個別バリデーションも設定されています。これは意図的であれば問題ありませんが、SignUpFormとの一貫性を確認してください。


良い点

  • zxcvbn-tsをSSR側に集約し、クライアントバンドルに4.7MB載せない設計は正しい
  • seqによるレースコンディション対策が堅実
  • テストカバレッジが高く、エッジケース(stale破棄、ネットワークエラー、非JSON応答)を網羅
  • WAI-ARIA属性(role="meter", aria-valuenow等)の対応
  • debounce: 300ms の妥当なバランス

総合

API連携は次PR予定のためスタブは許容範囲ですが、maxLengthガードのTODOhasSubmittedの未使用 はこのPRで解消すべきです。残りは軽微で、次PRで対応可能なものばかりです。

New%20session%20-%202026-06-16T16%3A49%3A44.813Z
opencode session  |  github run

@yupix

yupix commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Q. hasSubmitted ref が宣言されているが使われていない(SignUpForm.vue:26)
→ 将来の送信後メッセージ表示で使用予定のためTODOコメントを付与して保留。
Q. usePasswordStrength が256字超でも無駄なPOSTを送る(usePasswordStrength.ts:43)
→ バックエンドに現時点でmaxLength制約が未実装かつスコープ外のため、実装時の指針としてTODOコメントのみ追加。
Q. コードブロックに言語指定がない(auth-forms.md:48)
→ text を追加して修正済み。
Q. +server.ts という誤った参照(auth-forms.md:51)
→ elysia.ts に修正済み。
Q. PasswordInput に name prop がなくパスワードマネージャー連携が弱い
→ name propを追加し InputGroupInput へ転送するよう修正済み(ライブラリ側は無変更)。
Q. arkMessage() が SignInForm / SignUpForm で重複定義されている
→ src/lib/auth-validation.ts に切り出してimportに統一。ユニットテスト4件も追加済み。
未対応はスコープ外か次フェーズ案件

@yupix

yupix commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

/opencode review このPRをレビューして

@opencode-agent

Copy link
Copy Markdown
Contributor

PR Review: feat(front): auth forms

既に対応済みの指摘が多いですが、未解決の問題が3件残っています。


未解決の問題

1. console.log にパスワードが含まれるSignInForm.vue:21, SignUpForm.vue:31

console.log('signin stub', value);  // value.password が平文で出力される
console.log('signup stub', value);

PRコメントで「削除済み」と報告されていますが、実際には残っています。本番・開発問わず漏えいリスクがあるため、削除するか { email: value.email } のようにパスワードを除外してください。

2. usePasswordStrength のmaxLengthガード未実装usePasswordStrength.ts:31

// TODO: add client-side maxLength guard once backend enforces a limit (e.g. 256)

サーバーは maxLength: 256 で弾いていますが(elysia.ts:41)、クライアント側では超過してもPOSTが走り、400エラー後に前回のstrength値が残留します。以下を追加すべきです:

if (value.length > 256) {
  strength.value = '';
  return;
}

3. InputGroupAddon のフォーカス移譲InputGroupAddon.vue:24

currentTarget.parentElement?.querySelector('input')?.focus();

InputGroupTextarea 使用時にフォーカスが当たりません。[data-slot="input-group-control"] に変更してください。


軽微な指摘

4. hasSubmitted が未使用SignUpForm.vue:20

宣言・代入されていますが、テンプレートでもバリデーションタイミングでも参照されていません。PRコメントでは「将来のメッセージ表示用」とのことですが、ドキュメント (auth-forms.md:113) には「送信後は即時バリデーションに切り替える」と記載されており、実装と乖離があります。TODOコメントを明確化するか、ドキュメントを修正してください。

5. arkMessage の脆弱性auth-validation.ts

includes による部分マッチはarktypeのバージョンアップで壊れやすいです。将来的にarktypeのエラーツリーを直接カスタマイズする方が堅牢です(次PRで対応可能)。


良い点

  • zxcvbn-ts をSSR側に集約し、クライアントバンドルに4.7MB載せない設計
  • seq によるレースコンディション対策が堅実
  • テストカバレッジが高く、エッジケースを網羅
  • WAI-ARIA属性対応

総合

console.logのパスワード出力maxLengthガードはこのPRで解消すべきです。残りは軽微で次PR対応可能なため、この2件を修正すればマージ可能と思います。

New%20session%20-%202026-06-16T16%3A52%3A30.888Z
opencode session  |  github run

@yupix
yupix merged commit 9881ac3 into main Jun 16, 2026
3 checks passed
@yupix
yupix deleted the feat/front-auth-forms branch June 16, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/新機能 New feature or request priority/high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants