Skip to main content
RBAC bugs usually come from drift between backend authorization rules and frontend visibility rules. This guide shows a repeatable Playwright pattern to test both.

What to Cover

  • Positive access: user can see and perform allowed actions.
  • Negative access: user cannot view page, button, or API-backed action.
  • Escalation attempts: direct URL navigation and API-triggering UI actions are blocked.

Model Roles as Playwright Projects

Use one project per role so each role has explicit storageState.
playwright.config.ts
import { defineConfig } from '@stablyai/playwright-test';

export default defineConfig({
  projects: [
    {
      name: 'admin',
      use: { storageState: 'playwright/.auth/admin.json' },
      stably: { notifications: { slack: { channelName: '#rbac-alerts' } } },
    },
    { name: 'manager', use: { storageState: 'playwright/.auth/manager.json' } },
    { name: 'viewer', use: { storageState: 'playwright/.auth/viewer.json' } },
  ],
});

Write a Permission Matrix

Encode expected permissions once, then reuse in tests.
tests/rbac/permissions.ts
export const permissions = {
  admin: { canManageBilling: true, canDeleteProject: true },
  manager: { canManageBilling: false, canDeleteProject: true },
  viewer: { canManageBilling: false, canDeleteProject: false },
} as const;

Assert Both UI and Server Outcomes

tests/rbac/billing.spec.ts
import { test, expect } from '@playwright/test';

test('viewer cannot access billing settings', async ({ page }) => {
  await page.goto('/settings/billing');
  await expect(page.getByText('Access denied')).toBeVisible();
  await expect(page).toHaveURL(/forbidden|access-denied/);
});

test('manager cannot see delete workspace button', async ({ page }) => {
  await page.goto('/settings/general');
  await expect(
    page.getByRole('button', { name: 'Delete workspace' }),
  ).toHaveCount(0);
});

High-Value RBAC Cases

  1. User downgraded from admin to viewer mid-session.
  2. Invite flow grants wrong default role.
  3. Protected API action succeeds from hidden-but-triggerable UI path.
  4. Cached permissions allow stale access after logout/login.
Run RBAC tests with strict isolation: no shared mutable account state between role projects.

Stably Features to Use for RBAC Coverage

npx stably cloud test --project=admin --project=manager --project=viewer --env=Staging