Stop 10 · Authentication & AuthorizationTopics · 6/11

Role-Based Access Control (RBAC)

Part of the Backend Developer Roadmap

In short

Being logged in isn't the same as being allowed. A student can view marks, a teacher can edit them, and only an admin can delete a course. RBAC writes those rules down once.

Restricting actions or resources based on a user's assigned role (admin, editor, viewer) rather than just whether they're logged in at all.

The blob character holds a keyring with three coloured keys in front of three matching doors.

Think of it like this

The hostel keyring

The warden has keys to every room. Mess staff get the kitchen and store room. Students get only their own room. Nobody hands out keys person by person; keys come with the job. RBAC works the same: permissions belong to roles, and users get roles.

Watch it happen

Roles and what they can do

Roles
admin
teacher
student
course:create
course:delete
marks:edit
marks:view

1/5Three roles. Each user is given one or more of them.

The big ideas

What you need to know

Users get roles

Riya is a teacher. You never attach permissions to Riya directly, so changing a role updates everyone who has it.

Roles get permissions

Name permissions as resource:action, like marks:edit. Check permissions in code, not role names.

Check on the server

Hiding a button isn't security. Every protected route must check the permission on the backend.

Deny by default

If a route forgets to declare a permission, refuse the request instead of allowing it.

See it in code

Walk through the code

ts
1const ROLE_PERMISSIONS: Record<string, string[]> = {2  student: ["marks:view"],3  teacher: ["marks:view", "marks:edit"],4  admin: ["marks:view", "marks:edit", "course:create", "course:delete"],5};6 7export function requirePermission(permission: string) {8  return (req: Request, res: Response, next: NextFunction) => {9    const roles: string[] = req.user?.roles ?? [];10    const allowed = roles.some((r) => ROLE_PERMISSIONS[r]?.includes(permission));11    if (!allowed) return res.status(403).json({ error: { code: "FORBIDDEN" } });12    next();13  };14}15 16app.patch("/marks/:id", requireAuth, requirePermission("marks:edit"), updateMarks);

1/4Each role maps to a list of permissions. In bigger apps this lives in the database.

requireAuth sets req.user from the session or token. Request, Response and NextFunction are imported from express.

Save yourself the pain

Common mistakes

Oops

Writing if (user.role === 'admin') all over the codebase.

Do this instead

Check permissions like marks:edit instead. Adding a new role then means editing one table, not fifty if statements.

Oops

Only hiding the Delete button for non-admins in the frontend.

Do this instead

Anyone can call the API with curl. The backend route must check the permission too.

Your mission

Lock down three routes

~30 min

Quick check

A logged-in student calls DELETE /courses/5. Which status should the API return?

Go deeper

  1. 1Add a role field to your user model and gate one route by it.
  2. 2Build a reusable middleware that checks the required role before allowing a request through.
  3. 3Design roles with the principle of least privilege — grant only what's needed.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge