fix: use BYPASSRLS session for all auth deps and user-mutation endpoints

Phase 4 enabled RLS on the users table. All code paths that touch users
(or other RLS-protected tables) before require_tenant_context sets
app.current_account_id must use get_admin_db (BYPASSRLS):

- deps.py: get_current_user and get_current_active_user → get_admin_db
- auth.py: all endpoints → get_admin_db (login, register, refresh, etc.
  run before tenant context exists; mutation endpoints also need session
  consistency since current_user is in the admin session)
- accounts.py: transfer_ownership, leave_account, delete_account
  → get_admin_db (these mutate current_user directly)
- onboarding.py: dismiss_onboarding → get_admin_db (same reason)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-12 03:25:18 +00:00
parent 501442e5f0
commit 3ff886363c
4 changed files with 32 additions and 22 deletions

View File

@@ -24,10 +24,14 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
async def get_current_user(
db: Annotated[AsyncSession, Depends(get_db)],
db: Annotated[AsyncSession, Depends(get_admin_db)],
token: Annotated[str, Depends(oauth2_scheme)]
) -> User:
"""Get current authenticated user from JWT token."""
"""Get current authenticated user from JWT token.
Must use get_admin_db (BYPASSRLS): this dep runs before require_tenant_context
sets app.current_account_id, so the users table RLS would block the lookup.
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
@@ -77,10 +81,14 @@ async def get_refresh_token_payload(
async def get_current_active_user(
request: Request,
current_user: Annotated[User, Depends(get_current_user)],
db: Annotated[AsyncSession, Depends(get_db)],
db: Annotated[AsyncSession, Depends(get_admin_db)],
) -> User:
"""Ensure user is active (not disabled). Auto-downgrades expired trials.
Enforces must_change_password — blocks all routes except allowlist."""
Enforces must_change_password — blocks all routes except allowlist.
Uses get_admin_db: runs before require_tenant_context sets the ContextVar,
so tenant-scoped tables (subscriptions) would return 0 rows via app role.
"""
if not current_user.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,