Authentication vs. Authorization: A Quick Refresher

It's crucial to understand the difference between these two concepts:

  • Authentication (AuthN): Proving you are who you say you are. This is the login process.
  • Authorization (AuthZ): Determining if you have permission to do what you are trying to do. This happens after you are logged in.

Are you allowed to view this page? Can you edit this document? Can you delete another user? Answering these questions is the job of your authorization system.

Pattern 1: Role-Based Access Control (RBAC)

RBAC is the most common and intuitive authorization model. It's built around three core concepts: Users, Roles, and Permissions.

The logic is simple:

  1. You define a set of Permissions (e.g., create:article, delete:user, view:dashboard).
  2. You define a set of Roles (e.g., admin, editor, viewer).
  3. You assign Permissions to Roles (e.g., the editor role has the create:article permission).
  4. You assign Roles to Users (e.g., Alice is an editor).

Therefore, Alice can create an article because she has the editor role, which has the create:article permission. This model is straightforward to manage and understand.

Code Snippet: An RBAC Middleware in Node.js/Express Let's create a simple middleware that checks if a user has the required role to access a route.

JavaScript


// A simple database mock
const users = { '123': { id: '123', name: 'Alice', role: 'admin' } };

// Middleware to check for the session and user
function checkAuth(req, res, next) {
  const user = users[req.session.userId];
  if (user) {
    req.user = user;
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
}

// THE RBAC MIDDLEWARE
function checkRole(requiredRole) {
  return (req, res, next) => {
    // This assumes the 'checkAuth' middleware has already run
    // and attached the user object to the request.
    if (req.user && req.user.role === requiredRole) {
      next(); // User has the role, proceed!
    } else {
      res.status(403).send('Forbidden: Insufficient role');
    }
  };
}

// --- Applying the middleware to routes ---
// Everyone who is logged in can view posts.
app.get('/posts', checkAuth, (req, res) => res.send('List of posts'));

// ONLY users with the 'admin' role can access the admin dashboard.
app.get('/admin/dashboard', checkAuth, checkRole('admin'), (req, res) => {
  res.send('Welcome to the admin dashboard!');
});

Pattern 2: Attribute-Based Access Control (ABAC)

While RBAC is powerful, it can become rigid. What if you need more dynamic rules? For example, "An editor can only edit their own articles, and only if the article's status is 'draft'." This is where ABAC shines.

ABAC makes authorization decisions based on policies that evaluate attributes (or characteristics). These attributes can come from anywhere:

  • User Attributes: Role, department, security clearance, age.
  • Resource Attributes: The document's owner, status, creation date, sensitivity level.
  • Action Attributes: The action being performed (e.g., read, write, delete).
  • Environmental Attributes: Time of day, user's location (IP address), device being used.

An ABAC policy engine takes these attributes and evaluates a set of rules to produce a "Permit" or "Deny" decision.

Conceptual Example of an ABAC Policy

PERMIT an action (e.g., 'edit')
ON a resource (e.g., 'article')
IF user.role == 'editor'
AND user.id == resource.authorId
AND resource.status == 'draft'
AND environment.timeOfDay IS BETWEEN '09:00' AND '17:00'

ABAC vs. RBAC

  • RBAC: Simpler to set up and manage for applications with a few, well-defined user types. It's static.
  • ABAC: Infinitely more flexible and powerful. It excels in complex systems with dynamic, fine-grained access rules. It's dynamic and context-aware, but also more complex to design and implement.

For most standard web applications, RBAC is a great starting point. If you find yourself creating dozens of roles just to handle minor variations in permissions, it might be time to consider ABAC.