CodingNic

Authorization & Roles

Exercises

Authorization & Roles 30 min read

Exercises

Objectives

By the end of this lesson, you should be able to:

  • Build an ownership-checked edit route
  • Build a role-checked admin route
  • Confirm both correctly distinguish authentication failures from authorization failures

⚠️ A note on verification: every command and every output in this lesson was actually run, with real HTTP requests against a real Express app.

Exercise: Posts and an Admin Panel

a) Build an edit route. PUT /api/v1/posts/:id, following Lesson 3’s ownership pattern, isOwner || isAdmin, otherwise 403:

javascript
app.put('/api/v1/posts/:id', requireAuth, (req, res) => {
  const post = posts.find(p => p.id === Number(req.params.id));
  if (!post) return res.status(404).json({ error: 'NotFound', message: 'Post not found' });
  const isOwner = post.authorId === req.user.userId;
  const isAdmin = req.user.role === 'admin';
  if (!isOwner && !isAdmin) {
    return res.status(403).json({ error: 'Forbidden', message: 'You can only edit your own posts' });
  }
  post.title = req.body.title;
  res.json(post);
});

b) Build an admin-only route. GET /api/v1/admin/users, following Lesson 2’s role pattern, listing every user, requireAuth and requireRole('admin'):

javascript
app.get('/api/v1/admin/users', requireAuth, requireRole('admin'), (req, res) => {
  res.json(users.map(u => ({ id: u.id, email: u.email, role: u.role })));
});

c) Test editing someone else’s post:

text
JORDAN EDITS ERIN POST: 403 {"error":"Forbidden","message":"You can only edit your own posts"}

d) Test editing your own post:

text
JORDAN EDITS OWN POST: 200 {"id":2,"title":"Updated title","authorId":2}

e) Test a non-admin listing users:

text
MEMBER LISTS USERS: 403 {"error":"Forbidden","message":"Requires admin role"}

f) Test an admin listing users:

text
ADMIN LISTS USERS: 200 [{"id":1,"email":"erin@example.com","role":"member"}, ...]

g) A permissions-based extension. Roles work well until requirements get more specific (“editors can publish posts, but not delete users”). Sketch, in words or in a comment, what a permissions: ['posts:edit', 'posts:delete'] array on a user, checked with a requirePermission('posts:delete') middleware, would look like, and explain one advantage it has over a single role string for a larger application.

Recap

This module covered authorization as a distinct concern from authentication: the difference between the two, role-based middleware for broad permissions, and ownership-based checks for “can this specific user act on this specific resource,” a pattern that shows up constantly in real CRUD APIs.

Next module: input validation, making sure the data reaching every one of these routes is valid before any of this logic even runs.