Topic 6B.7
Authorization: RBAC, ABAC, ReBAC & Multi-Tenant Access
In one line
Authorization answers 'is this caller allowed to do this to that?'. Roles are the simple start, attributes add context, relationships model sharing, and every check must also enforce tenant boundaries.
Think of it like this
An office building. RBAC is job-based access ('managers can enter the meeting rooms'). ABAC adds conditions ('...but only during working hours, and only on their own floor'). ReBAC is relationship-based ('you can enter a room if you're invited to the meeting in it'), like sharing a Google Doc.
Key ideas
- 01
RBAC (role-based): users get roles (
admin,editor,viewer), roles grant permissions (order:refund). Simple and auditable; breaks down when rules depend on data ('only refund orders from your own store'), leading to 'role explosion'. - 02
ABAC (attribute-based): decisions from attributes of the user, the resource, the action, and the context (
user.storeId == order.storeId && order.amount < 5000 && time within business hours). Very flexible; policies are usually written in a policy language (OPA/Rego, Cedar) and evaluated by a policy engine. - 03
ReBAC (relationship-based): permissions follow a graph of relationships (user → member of → team → owner of → folder → contains → document). Google's Zanzibar paper popularised it; OpenFGA and SpiceDB implement it. Ideal for sharing and hierarchical permissions at scale.
- 04
The most common real bug is BROKEN OBJECT-LEVEL AUTHORIZATION: checking that the user is logged in but not that THIS order belongs to them (
GET /orders/9002returns someone else's order). Always check ownership/tenant on every object access, ideally centrally (a policy layer, or tenant-scoped queries that make cross-tenant access impossible, e.g. PostgreSQL row-level security).
Java / Spring map
- →
Spring Security's
@PreAuthorize("hasAuthority('order:refund') and @orderPolicy.canRefund(authentication, #orderId)")combines a role/permission check with an object-level policy bean.
Code & diagrams
Scope the query by tenant/owner so a guessed ID can never return someone else's data.
@GetMapping("/orders/{id}")
Order get(@PathVariable long id, @AuthenticationPrincipal Jwt jwt) {
String tenant = jwt.getClaimAsString("tenant_id");
return orders.findByIdAndTenantId(id, tenant) // not findById(id)!
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}Explain without notes
Why return 404 rather than 403 when a user requests another tenant's order?
Practice
Design authorization for a document-sharing app: owners, editors, viewers, shared folders, and organisation admins.
Trade-offs
- ↔
RBAC: simple, coarse. ABAC: expressive, harder to audit. ReBAC: models sharing and hierarchy well, but requires a dedicated authorization service and consistency care.
Run it in production
You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:
Completion checklist
I check object ownership/tenant on every access
I can choose between RBAC, ABAC, and ReBAC for a product