Guides / Prompting and shipping

How to Add Role-Based Access Control (RBAC) to Your App

Add roles and permissions to your app safely: pick a model, store roles, check them on the server for every request, and test that users can't see others' data.

Mythex Team · 2026-09-29 · 6 min read

To add role-based access control, give each user one or more roles (for example admin, editor, viewer), define which actions each role may perform, and check those permissions on the server for every request — not just by hiding buttons. Also check that the record being accessed belongs to the user's account or team. Start with a few fixed roles in your database, and test by trying to reach another user's data directly.

Authentication vs authorisation

Two words that get mixed up:

  • Authentication is who are you? — logging in. See how to add login to your app.
  • Authorisation is what are you allowed to do? — which pages, records and actions a logged-in user can reach.

RBAC is the most common way to do authorisation. It matters: as of September 2026, Broken Access Control is #1 in the OWASP Top 10:2025, the widely used list of the most serious web application security risks. Typical failures are users reading other users' records by changing an ID in the URL, or ordinary users calling admin-only API routes.

Choose a model

ModelHow it worksGood forTrade-off
Single role columnusers.role = admin / memberSmall apps with two or three rolesGets messy when people need different roles in different places
Roles + permissionsRoles map to named permissions (invoices.approve); code checks permissionsApps whose rules will growMore tables; a permissions list to maintain
Per-team (multi-tenant) rolesA membership table: user + organisation + roleSaaS where people belong to teams or workspacesEvery query must be scoped to the organisation
Attribute or relationship basedRules on record attributes ("the author can edit their own post") or on relationships (shared with)Documents, sharing, complex workflowsHarder to reason about; consider a dedicated library or service
Auth provider rolesYour login provider stores roles or organisation membershipsApps already using a hosted auth providerTied to that provider; still enforce on your server

In practice most apps combine two things: roles for what kind of actions someone can take, and ownership for which records they can take them on. "Editors can edit posts" is a role rule; "…posts in their own workspace" is an ownership rule. You need both.

Start simpler than you think. Admin, member and viewer covers a surprising number of apps. It is easier to split a role later than to untangle ten roles nobody understands.

Design the roles before writing code

Write a permission table in plain language first. It doubles as the spec for the AI and as your test plan:

ActionOwnerAdminMemberViewer
View projects in the team✓✓✓✓
Create and edit projects✓✓✓
Delete projects✓✓
Invite and remove people✓✓
Change roles✓
Billing✓

Decide the edge cases too: can the last owner leave? Can an admin remove the owner? What does a removed user see?

Step by step

1. Have working login first

RBAC sits on top of authentication. The server must know reliably who is making each request, from a session or a verified token — never from a user ID the browser sends in the request body.

2. Store roles in the database

For a single-team app, a role column on users is fine. For teams, use a memberships table:

create table memberships (
  user_id uuid references users(id),
  organization_id uuid references organizations(id),
  role text not null check (role in ('owner','admin','member','viewer')),
  primary key (user_id, organization_id)
);

Never let users set their own role through a normal profile update. Role changes go through a dedicated, permission-checked endpoint.

3. Check permissions on the server, in one place

Write one helper — something like can(user, 'projects.delete', project) — and call it in every API route, server action and background task that reads or changes data. One central function is easier to review and test than checks scattered through the code. Deny by default: if no rule allows an action, it is refused.

4. Scope every query

Every query for team data should include the team: where organization_id = :currentOrg, not just where id = :id. This is what stops someone from changing /projects/123 to /projects/124 and seeing another company's project. Return "not found" rather than "forbidden" for records in other teams, so you don't reveal they exist.

5. Add database-level protection where it counts

Postgres row-level security (RLS) lets the database enforce which rows each user can see. It is essential if the browser talks to the database directly (as with some hosted backends), and a useful second layer for sensitive tables otherwise.

6. Reflect roles in the interface

Now hide or disable what users can't do, so the app is not confusing. This is for usability only; the server checks do the protecting.

7. Log sensitive actions

Record who changed roles, deleted data or exported records, and when. An audit log helps with support questions and incidents.

8. Test like an attacker

Log in as each role and try every action in the permission table. Then try the things that shouldn't work: call admin API routes as a viewer, change IDs in URLs and requests, remove yourself as the last owner. Automated tests for the permission table are worth writing once the rules settle.

Prompts that work

Add role-based access control. Roles per organisation: owner, admin, member, viewer, stored in a memberships table. Permissions are in this table: [paste your table]. Create one server-side can(user, action, resource) helper, deny by default, and use it in every API route. Scope every query by organization_id. Hide actions the user can't perform in the UI, but never rely on that for security.

Review every API route and server function in this app. For each, list what data it reads or changes, whether it checks the user is logged in, whether it checks their role, and whether it scopes the query to their organisation. Fix any that are missing a check, and show me the list.

Add an admin-only /admin page where the owner can change members' roles. Prevent removing or demoting the last owner, log every role change with who made it and when, and make sure the role-change endpoint rejects anyone who isn't an owner.

Common mistakes

  • Checking roles only in the front end. The API is still open to anyone who calls it.
  • Trusting IDs from the client — a userId or role in the request body instead of the session.
  • Unscoped queries that fetch by record ID alone.
  • Roles checked on pages but not on API routes, exports, file downloads or webhooks.
  • Admin by email address hard-coded in the code (if email === 'me@...'), which is easy to forget and hard to change.
  • Too many roles too early, which nobody can explain six months later.
  • Stale sessions: a demoted user keeps admin rights until they log out. Check roles from the database, or refresh them, on sensitive actions.

RBAC checklist

  • Permission table written in plain language, edge cases decided
  • Server knows the user from the session, never from request data
  • Roles stored in the database; users can't change their own
  • One central permission check, deny by default, used in every route
  • Every query scoped to the user's organisation or ownership
  • Row-level security where the browser can reach the database
  • UI hides what users can't do (for usability only)
  • Sensitive actions logged
  • Every role tested, including direct API calls and changed IDs

Access control is one item on a longer list; the security checklist for AI-built apps covers the rest. If you are building a tool for your own staff, how to build an internal tool shows where roles fit.

RBAC on Mythex

Mythex doesn't provide a built-in login or roles system for your app's users. You choose an auth library or provider and describe the roles, and the agent wires them into your code and your project's Postgres database (add login to your app). Mythex's own workspace roles, such as Owner and Member, control who can edit your Mythex projects, not who can use the app you publish. Use Plan mode to agree the permission table before building, then use the review prompt above and test every role in Preview before you publish. The docs' security prompts are a useful follow-up.

Questions

What is role-based access control?

Role-based access control (RBAC) decides what each user can do based on the roles they hold, such as admin, editor or viewer. Permissions are attached to roles rather than to individual people, so changing what someone can do is a matter of changing their role.

What is the difference between authentication and authorisation?

Authentication checks who someone is, usually by logging in. Authorisation decides what that logged-in person is allowed to see and do. RBAC is one way of doing authorisation.

Is hiding buttons in the interface enough to restrict access?

No. Anyone can call your API directly, so hiding a button only changes what people see. Every request that reads or changes data must be checked on the server against the user's role and ownership of the record.

Should I use Postgres row-level security?

Row-level security lets the database itself enforce which rows each user can read or write, which is a strong safety net, especially when the front end talks to the database directly. It adds complexity, so many apps enforce permissions in server code and use row-level security as an extra layer for sensitive tables.

Keep reading

  • How to Add a Blog to Your Website: Options, SEO and Setup — How to add a blog to your website: Markdown files vs a built-in editor vs a CMS, subfolder vs subdomain, SEO basics, and prompts to build it with AI.
  • How to Add a Contact Form to Your Website (That Actually Reaches You) — How to add a contact form that works: save messages, get email alerts, stop spam, and avoid the mistakes that silently lose enquiries. Prompts included.
  • How to Add a Database to Your App (Without Losing Data Later) — How to add a database to an app you built: when you need one, Postgres vs hosted options, designing tables, prompts to use, and mistakes that lose data.
  • How to Add AI Features to Your App — Add summaries, chat, data extraction and classification to your app with an LLM API — keeping keys safe, costs under control and output trustworthy.
  • How to Add Analytics to Your App: GA4, Privacy-First Tools and Product Analytics — How to add analytics to your website or app: Google Analytics 4 vs privacy-first vs product analytics, what to track, cookie consent, and prompts to use.
  • How to Add Cookie Consent to Your Website — Add a cookie banner that actually blocks scripts until people agree: what needs consent, CMP vs custom, Google consent mode and a checklist. Not legal advice.

Start building free · Templates · Docs