Skip to content

feat: rebuild the membership workflow on Discord-only login - #61

Merged
yufoxda merged 6 commits into
developfrom
agent/membership-workflow
Jul 27, 2026
Merged

feat: rebuild the membership workflow on Discord-only login#61
yufoxda merged 6 commits into
developfrom
agent/membership-workflow

Conversation

@yufoxda

@yufoxda yufoxda commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

概要

入部ワークフロー一式を実装し、ログインを Discord のみ に統一します。ログインに使う Discord アカウントが、そのまま入部申請で検証する対象サーバーの在籍確認に使われるため、連携・突合が必要な 2 つ目のプロバイダが存在しません。GitHub ログインと関連する資格情報は削除しました。

主な変更

認証

  • Discord を唯一の ID プロバイダに変更(disableSignUp を解除し、GitHub プロバイダを削除)
  • scope: ["guilds"] は Better Auth の既定 identify+email追加される実装のため、ログインと在籍確認の双方が成立
  • 未使用になった GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET を型定義と env サンプルから削除

入部ワークフロー

  • 申請 → 却下 → 再申請 → 承認 → 本人編集を、バージョン管理された 1 行で扱い、楽観ロックで競合を防止
  • 承認時に Discord で検証済みかつ鮮度のある在籍証跡を必須化。証跡が古い/無い場合は有効化せずエラー
  • 名簿・管理画面は許可リストのフィールドのみ公開。CSV 出力はユーザー入力由来のセルの数式インジェクションを無害化

DB / ランタイム堅牢化

  • 認可状態をトランザクションローカルに限定(短いトランザクション内で RLS ロールと SET LOCAL による識別子を設定)
  • Discord / OAuth 通信はトランザクション外。低速な上流が Hyperdrive の origin 接続を占有しないようにする
  • superuser / BYPASSRLS / app_rls を使えないログインを起動時に拒否(fail closed)
  • マイグレーション、pgTAP テスト、専用ランタイムロール作成と監査済みテストデータ削除のランブックを追加

検証

対象 結果
community テスト 36 pass / 1 skip(live Discord)、tsc --noEmit クリーン
member テスト 17 pass、tsc --noEmit クリーン
frontend テスト 25 pass、lint エラー 0(既知の TanStack Table warning 1 件)、本番ビルド成功

デプロイ時の注意

本番反映は docs/hyperdrive-security.md の手順に従ってください(テストデータの監査付き削除 → マイグレーション適用 → 専用ランタイムロール作成 → Hyperdrive の資格情報差し替え)。マイグレーションは対象テーブルが空でない限り実行を拒否します。

既知の残課題(別 PR)

  • join ページの active → /me リダイレクトを useEffect からサーバー側へ移す(現状は一瞬フラッシュする)
  • NODE_ENV=development 時の認証バイパス(DEV_USER_ID)を本番で到達不能にする

🤖 Generated with Claude Code

yufoxda and others added 2 commits July 27, 2026 14:32
Sign-in now uses Discord as the sole identity provider. The account that signs
a member in is the same account the join flow verifies against the target
guild, so there is no second provider to link, reconcile, or keep in sync.
GitHub sign-in and its credentials are removed.

Membership workflow:
- One versioned member row carries an application through submit, reject,
  resubmit, approval, and self-service edits, guarded by optimistic locking.
- Approval requires fresh, Discord-verified evidence of target-guild
  membership; stale or missing evidence fails the request instead of
  activating the member.
- Directory and admin views expose only allowlisted profile fields, and CSV
  export neutralizes spreadsheet formulas in user-controlled cells.

Database and runtime hardening:
- Authorization state stays transaction-local: every protected unit opens a
  short transaction, sets the RLS role and request identity with SET LOCAL,
  and commits. Discord and OAuth calls happen outside those transactions so a
  slow upstream cannot pin a Hyperdrive origin connection.
- Worker startup rejects a database login that is a superuser, has BYPASSRLS,
  or cannot use app_rls.
- Adds the membership migration, pgTAP coverage, and runbooks for provisioning
  the dedicated runtime login and purging audited test data.

See docs/hyperdrive-security.md for the production change order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The RLS boundary and the directory allowlist are enforced in SQL, but nothing
executed supabase/tests, so 642 lines of security assertions never ran. CI now
replays every migration into a fresh database and runs the pgTAP suites, which
also catches a migration that only applies to an already-populated schema.

Reads that belong to one response now share one transaction. Loading an admin
member detail opened three transactions (row, Discord evidence, status
history) and the admin list opened two; each was a separate Hyperdrive round
trip with no consistency between them. They contain no external calls, so
merging them keeps the short-transaction rule from docs/hyperdrive-security.md
while giving each response a single snapshot.

Also drops the middleware debug line that logged every request path with the
names of all cookies, and removes an admin branch that its own follow-up
already covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yufoxda
yufoxda force-pushed the agent/membership-workflow branch from 07fab6d to e7906b6 Compare July 27, 2026 05:34
yufoxda and others added 2 commits July 27, 2026 14:40
The migration guards the table replacement with `lock table` and an emptiness
check, but the runner applies statements in autocommit, so the lock failed with
25P01 and the migration aborted partway. The PGlite replay hid this because it
executes the whole file as a single implicit transaction.

Opening the transaction explicitly restores the intended guarantee: the lock,
the emptiness check, and the replacement now commit or roll back together. The
replay test ends the aborted transaction after the expected refusal, which is
what a real runner does before the next attempt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pg_prove aborted 002 at the nullability assertion because is_nullable is
information_schema.yes_or_no and pgTAP's polymorphic is() found no match
against a text expectation. The suite planned 24 tests but stopped after 16,
so the eight assertions behind it — including the directory view's private
column allowlist — were never evaluated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
yufoxda and others added 2 commits July 27, 2026 16:45
Resolving "latest" makes the setup action query the GitHub API without
credentials, which returns a rate-limit error as soon as a few pull requests
build at once. The job then failed in eight seconds having tested nothing, and
the teardown step turned that into a second, misleading error.

The CLI version is pinned, and teardown no longer decides the job result.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…orkflow

# Conflicts:
#	frontend/package-lock.json
#	frontend/package.json
@yufoxda
yufoxda merged commit 101c900 into develop Jul 27, 2026
3 checks passed
@yufoxda
yufoxda deleted the agent/membership-workflow branch July 27, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant