Security Issues
OAuth auto-provisioning defaults to admin role — privilege escalation
OAuth Auto-Provisioning Defaults to Admin Role
Severity: P0 (privilege escalation) · Applies to: Supabase Auth + custom OAuth provisioning callback Related: ADR-039 — Supabase Auth confirmation and hardening · Authentication runbook
Problem
First-time OAuth users (Google, GitHub) were automatically assigned
account_role="admin" during provisioning. This gave every new external user
full account-level privileges immediately upon first login.
Symptoms:
- Any user authenticating via OAuth gets admin access without invitation
- New users can create unlimited workspaces, invite/remove users
- No separation between “first user” (should be admin) and “subsequent users”
Root Cause
In provision_oauth_user(), the role defaulting logic was:
role = account_role or "admin" # Every new user becomes adminThis was a shortcut during initial development — the intent was “first user should be admin” but the implementation made ALL users admin.
Solution
Changed the default to "member" (least-privilege):
# Before (dangerous)role = account_role or "admin"
# After (least-privilege)role = account_role or "member"Location: packages/core/src/spectral/infrastructure/auth/supabase_auth.py:294
Implementation Notes
- The
memberrole has no account-level privileges — only workspace membership - Workspace access requires explicit invitation from a workspace owner
- Admin provisioning should be done through a separate administrative flow
- The first user of a new account can be promoted to admin via Supabase Studio or direct database access
Prevention
Best Practices
- Default to least-privilege — new users should have minimal access
- Separate “create account” from “grant admin” — these are different operations
- Explicit role assignment — roles should never be implied by authentication method
- Test the default — unit tests should verify the default role is not elevated
Warning Signs
- Any code path where role defaults to
"admin"or"owner"without explicit check - User provisioning functions that don’t require an existing admin to authorize
- OAuth callbacks that grant more access than email/password registration
Related Tests
def test_provisions_new_user_as_member(self): """New OAuth users get member role, not admin.""" role = provision_oauth_user(user_id) assert role == "member" # Least-privilege defaultReferences
- Fixed in session 2026-03-21 (review findings)
- OWASP: Broken Access Control (A01:2021)