DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Designing RBAC with Claude Code: Role-Based Access Control, Policies, Audit Logging

Introduction

"Admins can view everything, but regular users only their own data" — implementing permissions ad hoc leads to gaps. Use RBAC to systematically manage permissions. Generate designs with Claude Code.


CLAUDE.md RBAC Rules

## RBAC Design Rules

### Role Design
- Principle of least privilege: grant only minimum necessary permissions
- Role hierarchy: admin > manager > member > guest
- Prohibit cross-tenant roles (manage roles per tenant)

### Permission Checks
- Always check roles in controller layer (not Service layer)
- Implement resource owner check separately (distinct from roles)
- Insufficient permissions return 403 (distinguish from auth error 401)

### Auditing
- Always record audit log for permission changes
- Log failed access attempts
- In production, require 2 approvals for permission changes
Enter fullscreen mode Exit fullscreen mode

Generated RBAC Implementation

// src/authorization/abilities.ts
import { AbilityBuilder, createMongoAbility } from '@casl/ability';

export function defineAbilitiesFor(user: AuthUser): AppAbility {
  const { can, cannot, build } = new AbilityBuilder<AppAbility>(createMongoAbility);

  switch (user.role) {
    case 'admin':
      can('manage', 'all');
      break;

    case 'manager':
      can('manage', 'Order');
      can('read', 'User');
      can('manage', 'Product');
      can('read', 'Report');
      cannot('delete', 'User');  // Managers cannot delete users
      break;

    case 'member':
      can('read', 'Order', { userId: user.id });  // Own orders only
      can('create', 'Order');
      can('update', 'Order', { userId: user.id, status: 'pending' });
      can('read', 'Product');
      break;

    case 'guest':
      can('read', 'Product');
      break;
  }

  return build({ detectSubjectType: (s) => s.__typename ?? s.constructor.name });
}
Enter fullscreen mode Exit fullscreen mode
// src/authorization/middleware.ts
export function authorize(action: Action, subjectType: Subject) {
  return async (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) return res.status(401).json({ error: 'Authentication required' });

    const ability = defineAbilitiesFor(req.user);
    const resourceId = req.params.id;

    if (resourceId) {
      const resource = await loadResource(subjectType, resourceId);
      if (!resource) return res.status(404).json({ error: 'Resource not found' });

      if (ability.cannot(action, subject(subjectType, resource))) {
        logger.warn({ userId: req.user.id, action, subject: subjectType, resourceId }, 'Access denied');
        return res.status(403).json({ error: 'Forbidden' });
      }

      req.resource = resource;
    } else {
      if (ability.cannot(action, subjectType)) return res.status(403).json({ error: 'Forbidden' });
    }

    next();
  };
}
Enter fullscreen mode Exit fullscreen mode

Apply to Routes

// All orders: admin + manager only
router.get('/orders', authenticate, requireRole('admin', 'manager'), async (req, res) => {
  const orders = await prisma.order.findMany();
  res.json(orders);
});

// Order detail: resource-based permission (owner or admin can view)
router.get('/orders/:id', authenticate, authorize('read', 'Order'), async (req, res) => {
  res.json(req.resource);
});

// User delete: admin only
router.delete('/users/:id', authenticate, authorize('delete', 'User'), async (req, res) => {
  await prisma.user.delete({ where: { id: req.params.id } });
  res.status(204).send();
});
Enter fullscreen mode Exit fullscreen mode

Summary

Design RBAC with Claude Code:

  1. CLAUDE.md — least privilege, no cross-tenant roles, audit permission changes
  2. Casl library — declarative role definitions (managed as code)
  3. subject() — pass resource instance for owner checks
  4. Log access failures as structured logs (use as audit trail)

Review RBAC designs with **Security Pack (¥1,480)* using /security-check at prompt-works.jp*

myouga (@myougatheaxo) — Axolotl VTuber.

Top comments (0)