feat: rebuild the membership workflow on Discord-only login - #61
Merged
Conversation
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
force-pushed
the
agent/membership-workflow
branch
from
July 27, 2026 05:34
07fab6d to
e7906b6
Compare
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>
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
入部ワークフロー一式を実装し、ログインを Discord のみ に統一します。ログインに使う Discord アカウントが、そのまま入部申請で検証する対象サーバーの在籍確認に使われるため、連携・突合が必要な 2 つ目のプロバイダが存在しません。GitHub ログインと関連する資格情報は削除しました。
主な変更
認証
disableSignUpを解除し、GitHub プロバイダを削除)scope: ["guilds"]は Better Auth の既定identify+emailに追加される実装のため、ログインと在籍確認の双方が成立GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRETを型定義と env サンプルから削除入部ワークフロー
DB / ランタイム堅牢化
SET LOCALによる識別子を設定)BYPASSRLS/app_rlsを使えないログインを起動時に拒否(fail closed)検証
tsc --noEmitクリーンtsc --noEmitクリーンデプロイ時の注意
本番反映は
docs/hyperdrive-security.mdの手順に従ってください(テストデータの監査付き削除 → マイグレーション適用 → 専用ランタイムロール作成 → Hyperdrive の資格情報差し替え)。マイグレーションは対象テーブルが空でない限り実行を拒否します。既知の残課題(別 PR)
joinページの active →/meリダイレクトをuseEffectからサーバー側へ移す(現状は一瞬フラッシュする)NODE_ENV=development時の認証バイパス(DEV_USER_ID)を本番で到達不能にする🤖 Generated with Claude Code