refactor(auth): #183 remove the unused custom oauth_states CSRF layer#184
Merged
Conversation
The custom OAuth-state CSRF machinery had ZERO production callers — the real
CSRF defense is Supabase's built-in OAuth2 `state` parameter (the client uses
flowType:'implicit' for this static export). The `oauth_states` table also
carried fully-public RLS (WITH CHECK/USING (true) on INSERT/SELECT/UPDATE), so
it was unused attack surface. Prod-verified before dropping: 0 rows (never
written to — nothing ever called generateOAuthState), 4 public policies.
Removed:
- src/lib/auth/oauth-state.ts + its test (generateOAuthState/validateOAuthState/
cleanupExpiredStates — 0 non-test importers)
- oauth_states CREATE TABLE + 3 indexes + 4 RLS policies + comments from the
monolithic migration (edited in place per CLAUDE.md — no separate migration)
- the deleteAllFromTable('oauth_states', ...) call in scripts/reset-database.ts
- the generated oauth_states type block from src/lib/supabase/types.ts
- Applied a live DROP TABLE oauth_states CASCADE to prod via the Management API;
verified the table + its 4 policies are gone. Full suite 3558 passed.
Docs: the OAuth blog post taught the removed manual-state pattern AND
mislabeled the flow as PKCE. Corrected it to the actual implicit-flow +
Supabase-state story (this app is a static export, so no exchangeCodeForSession
in the OAuth callback), fixed the matching OAuthButtons comment, and
regenerated ONLY the OAuth post's blog-data.json entry (other posts have a
separate pre-existing blog-data drift, left untouched to keep this PR scoped).
999_drop_all_tables.sql keeps its idempotent DROP as a no-op for older DBs.
Closes #183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #183. Removes a custom OAuth-state CSRF layer that had zero production callers and a table with fully-public RLS — unused attack surface.
Why
The real OAuth CSRF defense is Supabase's built-in OAuth2
stateparameter (the client usesflowType: 'implicit'— this is a static export, so no server-side code exchange). The customsrc/lib/auth/oauth-state.ts+oauth_statestable duplicated that (more weakly:session_idself-reported insessionStorage, fail-open) and nothing ever called it — both OAuth entry points comment that Supabase handles CSRF. The table's RLS wasWITH CHECK (true)/USING (true)on INSERT/SELECT/UPDATE: anyone with the anon key could flood/read/overwrite it.Prod-verified before dropping: 0 rows (never written to), 4 public policies. Dropped live via the Management API; confirmed table + policies gone.
Removed
src/lib/auth/oauth-state.ts+__tests__/oauth-state.test.ts(537 lines, 0 non-test importers)oauth_statesCREATE TABLE + 3 indexes + 4 RLS policies + comments from the monolithic migration (edited in place per CLAUDE.md — no separate migration file)deleteAllFromTable('oauth_states', ...)inscripts/reset-database.tsoauth_statestype block insrc/lib/supabase/types.tsDROP TABLE oauth_states CASCADEapplied to prod + verifiedDocs correction
The OAuth blog post taught the removed manual-state pattern and mislabeled the flow as PKCE. Corrected to the actual implicit-flow + Supabase-state story (a static export has no
exchangeCodeForSessionin the OAuth callback), and fixed the matchingOAuthButtons.tsxcomment. Regenerated only the OAuth post'sblog-data.jsonentry.blog-data.jsonhas a separate pre-existing drift — several other posts' committed titles/ids are stale vs their source.md(e.g. the offline-payment + E2E-encryption posts). Left untouched to keep this PR scoped; worth a standalonepnpm run generate:blogresync. Also left the email-verification callback'sexchangeCodeForSessionexample (different flow, not part of this removal).Verification
oauth_statestable + 4 policies dropped (verified viainformation_schema/pg_policies); OAuth sign-in unaffected (Supabasestatewas always the real path).pnpm type-check+pnpm lintclean. Full suite 3558 passed / 0 failed (−19 = the removed oauth-state tests).🤖 Generated with Claude Code