Skip to content

RBAC

OpenQuok's app-level roles/permissions and the recommended backend integration patterns.

2 min read

Connect your agent today

Draft from chat, review in your calendar, and publish only what you approve.

Start for $0

Overview

App-level RBAC is distinct from workspace membership. Roles and permissions are stored in the database and loaded by the backend when using requireFullAuthWithRoles.

There is no JWT auth hook; the app resolves public.users.id from the token and fetches roles/permissions from user_roles and role_permissions.

Roles

  • Editor — Can view and manage feedback; can view role/permission data. Use case: content moderators or support.
  • Admin — All editor permissions plus users.manage_roles (assign/remove roles). Only platform admins can assign/remove the admin role; admins can assign/remove only the editor role (enforced in RbacService and DB RPCs). Use case: platform admins.
  • Platform admin — Not a role; a flag on public.users (is_super_admin=true). Bypasses permission checks and can assign/remove any role.

Backend integration

Types

The role/permission types live in backend/data/types/rbacTypes.ts (for example AppRole and AppPermission).

Authenticate with roles

Use requireFullAuthWithRoles(supabase, userRepository, rbacRepository) so req.user includes:

  • roles
  • permissions
  • publicId
  • isPlatformAdmin

Use requireFullAuth only when the route does not need role/permission resolution.

Route guards

Use role/permission middlewares after requireFullAuthWithRoles:

  • requireEditor — editor, admin app role, or platform admin
  • requireAdmin — admin app role or platform admin
  • requirePlatformAdmin — platform admin only
  • requireRole('editor') — that app role or platform admin
  • requirePermission('feedback.view') — that permission (platform admin bypasses)
  • requireAnyPermission([...]) — at least one permission

Example: protect by permission

const authWithRoles = requireFullAuthWithRoles(supabase, userRepository, rbacRepository);
const requireManageRoles = requirePermission('users.manage_roles');
rbacRouter.post('/users/:userId/roles/:role', authWithRoles, requireManageRoles, rbacController.assignRole);
rbacRouter.delete('/users/:userId/roles/:role', authWithRoles, requireManageRoles, rbacController.removeRole);

Example: protect by editor or higher

feedbackRouter.get('/', authWithRoles, requireEditor, feedbackController.getAllFeedbacks);
feedbackRouter.patch('/:feedbackId', authWithRoles, requireEditor, feedbackController.handleFeedback);
Search documentation
Find a docs page
Discord Support