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.
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
- User downgraded from admin to viewer mid-session.
- Invite flow grants wrong default role.
- Protected API action succeeds from hidden-but-triggerable UI path.
- 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