Skip to content

feat(security): enable RLS and migrate to getClaims()#129

Merged
JDIZM merged 5 commits intomainfrom
feat/ws-rls-auth-standardization
Feb 2, 2026
Merged

feat(security): enable RLS and migrate to getClaims()#129
JDIZM merged 5 commits intomainfrom
feat/ws-rls-auth-standardization

Conversation

@JDIZM
Copy link
Owner

@JDIZM JDIZM commented Feb 1, 2026

WS-RLS: Auth Standardization

Changes

  • RLS Migration: Enable RLS on all tables (accounts, workspaces, profiles, workspace_memberships, audit_logs)
  • Replace jwt.verify() with supabase.auth.getClaims()
  • New key format: Use supabasePublishableKey and supabaseSecretKey (with legacy fallback)
  • Remove jwtSecret: No longer needed with getClaims()
  • Remove jsonwebtoken: Dependency no longer needed
  • Dual client pattern: supabase for auth, getSupabaseAdmin() for admin ops
  • README: Added Security section with detailed getClaims() explanation
  • .env.example: Updated with new key format
  • token-test.ts: Updated to use getClaims() instead of jwt.verify()

Security Model

┌─────────────────────────────────────────────────────────────┐
│ Data Queries: API → Drizzle ORM → PostgreSQL                │
│               (Bypasses RLS via DATABASE_URL)               │
├─────────────────────────────────────────────────────────────┤
│ Auth: API uses supabase.auth.getClaims() for JWT verify     │
│       (Works with publishable key, not affected by RLS)     │
├─────────────────────────────────────────────────────────────┤
│ Direct Access: Blocked by RLS (no policies = deny all)      │
│                If publishable key leaks, queries return []  │
└─────────────────────────────────────────────────────────────┘

Why getClaims() instead of jwt.verify()?

Old Approach New Approach
jwt.verify(token, JWT_SECRET) supabase.auth.getClaims(token)
Required SUPABASE_AUTH_JWT_SECRET env var No secret needed
Manual dependency on jsonwebtoken Built into Supabase SDK

How getClaims() works:

  1. Decodes JWT header to check algorithm
  2. Asymmetric keys (new sb_publishable_*): Fetches JWKS, verifies locally with Web Crypto API
  3. Symmetric keys (old eyJhbG...): Falls back to getUser() server call

Either way, no JWT_SECRET needed - verification is handled automatically.

Supabase Key Migration (2025+)

Old Name New Name Purpose
SUPABASE_ANON_KEY SUPABASE_PUBLISHABLE_KEY Auth operations
SUPABASE_SERVICE_ROLE_KEY SUPABASE_SECRET_KEY Admin operations
SUPABASE_AUTH_JWT_SECRET ❌ Not needed Replaced by getClaims()

Testing

# Apply RLS migration
supabase db push

# Verify RLS enabled
supabase db query "SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'"

# Run tests
pnpm test

WS-RLS: Auth Standardization

- Enable RLS on all data tables (deny-all policy)
- Replace jwt.verify() with supabase.auth.getClaims()
- Update to new key format (supabasePublishableKey, supabaseSecretKey)
- Remove jwtSecret dependency (not needed with getClaims)
- Add dual client pattern (supabase + getSupabaseAdmin)
- Add Security section to README
- Update .env.example with new key format

RLS blocks direct data queries via publishable key while:
- API data access via Drizzle (DATABASE_URL) bypasses RLS
- Auth endpoints (/auth/v1/*) unaffected by RLS

getClaims() benefits:
- Auto-detects key type (asymmetric/symmetric)
- Local verification with asymmetric keys (faster)
- No JWT_SECRET needed in env
@gemini-code-assist
Copy link

Summary of Changes

Hello @JDIZM, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application's security posture by standardizing authentication and implementing robust defense-in-depth mechanisms. It migrates JWT verification to Supabase's getClaims() method for improved security and flexibility, introduces a new key management strategy, and enables Row Level Security (RLS) on critical database tables to prevent unauthorized direct data access.

Highlights

  • Row Level Security (RLS) Enabled: RLS is now enabled on core database tables (accounts, workspaces, profiles, workspace_memberships, audit_logs) with a deny-all policy for direct access, enhancing defense-in-depth.
  • JWT Verification Migrated: The application now uses supabase.auth.getClaims() for JWT verification, replacing the previous jsonwebtoken.verify() method. This offers automatic key type detection and local verification for asymmetric keys.
  • Supabase Key Standardization: Environment variables for Supabase keys have been updated to SUPABASE_PUBLISHABLE_KEY and SUPABASE_SECRET_KEY, aligning with newer Supabase key formats, while maintaining backward compatibility. The SUPABASE_AUTH_JWT_SECRET is no longer required.
  • Dual Supabase Client Pattern: A new getSupabaseAdmin() function has been introduced to provide a separate Supabase client for administrative operations using the SUPABASE_SECRET_KEY, distinct from the public-facing client.
  • Security Documentation: The README.md has been updated with a dedicated "Security" section, detailing the RLS implementation, Supabase key usage, and token verification process.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant security enhancements by enabling Row Level Security (RLS) and migrating from JWT verification to Supabase's getClaims() method. The changes are well-structured, particularly the introduction of separate Supabase clients for regular and admin operations, which is a great practice. The documentation updates in the README are clear and comprehensive. My review includes a few suggestions to improve configuration robustness, project hygiene in .gitignore, and adherence to migration naming conventions.

JDIZM added 4 commits February 2, 2026 04:45
- Remove jsonwebtoken and @types/jsonwebtoken (no longer needed)
- Update token-test.ts to use getClaims() instead of jwt.verify()
- Expand README to explain how getClaims() works
- No JWT_SECRET environment variable needed anymore
- Remove personal settings from .gitignore (use global gitignore)
- Throw error if SUPABASE_PUBLISHABLE_KEY not set (fail fast)
- Remove legacy key references from .env.example
- Simplify config comment (remove '2025+ format' wording)
@JDIZM JDIZM merged commit 50ae6c2 into main Feb 2, 2026
3 checks passed
@JDIZM JDIZM deleted the feat/ws-rls-auth-standardization branch February 2, 2026 10:33
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