Modern applications demand flexible and context-aware access control models, especially in domains like logistics, supply chain, and finance. Traditionally, Role-Based Access Control (RBAC) has been the de facto model. However, RBAC alone often falls short when business rules require dynamic attributes such as organizational units, teams, or individual ownership identifiers.
This is where Attribute-Based Access Control (ABAC) complements RBAC, enabling fine-grained, policy-driven authorization decisions.
In this post, we will walk through how we combined RBAC and ABAC using Frontegg’s Node.js SDK and a sample Next.js logistics app, ensuring that:
- Senior managers can access all consignments within their organization.
- Managers can access consignments belonging to their team or those they own.
- Individual Contributors (ICs) can only access consignments they personally own.
We will also demonstrate how the same Frontegg session and token flow integrates seamlessly between our frontend Next.js app and our backend Node.js REST API, using Frontegg’s Node.js SDK for authentication and ABAC-style authorization middleware.
Why RBAC alone isn’t enough
RBAC assigns permissions based on static roles (Admin, Manager, User, etc.). While effective for many applications, it lacks the flexibility to make decisions based on runtime attributes such as:
- Individual ownership (ownerId)
- Team membership (teamId)
- Organization (orgId)
By combining RBAC with ABAC, we empower business-centric access models that reflect real-world logistics and supply chain hierarchies.
The RBAC and ABAC authorization model
Our sample project uses the following hybrid model:
Role | ABAC Rule |
Senior manager | Can view, edit, and delete all consignments in their organization (orgId ). |
Manager | Can view, edit, and delete consignments for their team (teamId ) or where they are owner (ownerId ). |
Individual contributor (IC) | Can only view, edit, and delete consignments they own (ownerId ). |
The following diagram illustrates our hybrid RBAC + ABAC decision flow:
- Role-based access is first checked (
SeniorManager, Manager, IC
). - Based on the role, additional attribute-based checks are enforced, such as matching
ownerId
,teamId
, ororgId
. - Only if the role and all applicable attribute checks pass will the user be granted access to the resource.
Technologies used
- Next.js (Frontend)
- Node.js / Express REST API (Backend)
- MongoDB (Database)
- Frontegg Node.js SDK
- Frontegg Next.js SDK
How it works
Backend protection and token validation
In the backend (app.ts
), we secure all /api
routes using Frontegg’s middleware:
app.use('/api',
withAuthentication(),
(req, res, next) => {
if (req.frontegg?.user) {
req.user = req.frontegg.user;
req.userId = req.frontegg.user.sub;
req.userTeamId = req.frontegg.user.metadata?.teamId ?? undefined;
req.userOrgId = req.frontegg.user.tenantId;
}
next();
},
consignmentRoutes
);
RBAC + ABAC enforcement in the controller layer
In our controllers, every sensitive operation first authorizes the user based on RBAC and ABAC rules, combining req.user.roles
and the consignment.orgId
, consignment.teamId
, or consignment.ownerId
.
function authorizeConsignment(user: any, consignment: IConsignment) {
if (user.roles.includes('SeniorManager')) {
return consignment.orgId === user.tenantId;
} else if (user.roles.includes('Manager')) {
return consignment.teamId === user.metadata?.teamId || consignment.ownerId === user.sub;
} else if (user.roles.includes('IC')) {
return consignment.ownerId === user.sub;
}
return false;
}
This centralized RBAC and ABAC enforcement ensures your business rules remain consistent, auditable, and easy to maintain.
Secure consignment creation
When creating a consignment, the ownerId
, teamId
, and orgId
are always derived from the authenticated user’s claims, preventing spoofing from client-side requests.
By always deriving sensitive attributes like ownerId
, teamId
, and orgId
from the authenticated user’s claims, the backend enforces trust boundaries. Clients cannot override these fields, reducing the attack surface.
export const createConsignmentController = async (req: Request, res: Response) => {
try {
const consignmentData = {
...req.body,
ownerId: req.user.sub,
teamId: req.user.metadata?.teamId,
orgId: req.user.tenantId
};
const consignment = await createConsignment(consignmentData);
res.status(201).json(consignment);
} catch (error) {
res.status(400).json({ message: 'Failed to create consignment', error: error?.message });
}
};
When a user with the SeniorManager
role creates a consignment via the API, the backend automatically enriches the payload with the user’s organization, team, and ownership attributes, ensuring trust and consistency.
{
"_id": "665d3eaf3fb8e9a6a210f4d3",
"product": "Test Product",
"quantity": 100,
"source": "Nairobi, Kenya",
"destination": "Paris, France",
"status": "Processing",
"ownerId": "6360a2d2-4418-4942-8d61-30a9c1328229",
"teamId": "teamA",
"orgId": "7b575a47-9519-4ae3-b548-395a24df6aee"
}
This response ensures that:
ownerId
is always tied to the authenticated user’ssub
claim.teamId
andorgId
are derived from the Frontegg session and cannot be spoofed by the client.- The consignment is now fully traceable and securely associated with the user’s organization and team.
Frontend: User authentication
Users authenticate on the frontend app using Frontegg’s Next.js SDK, receiving a JWT Access Token containing:
sub
(User ID)tenantId
(Organization ID)metadata.teamId
(Team ID)roles
This token is sent with every API request using the Authorization: Bearer
header.
Frontend: Passing the access token to the backend
In the Next.js frontend, we use the useAuth()
hook to retrieve the accessToken
, which is attached to API calls:
const { accessToken } = useAuth();
const res = await axios.get('/api/consignments', {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
Conclusion
By combining Frontegg’s role management with attribute-aware authorization logic on the backend, we demonstrated how modern authorization models can:
- Support hierarchical roles like Senior managers, Managers, and ICs
- Enforce dynamic attribute checks (e.g.,
ownerId
,teamId
,orgId
) in your API - Prevent client-side spoofing of sensitive attributes
- Keep your authorization logic clean, auditable, and scalable
With Frontegg handling the heavy lifting of authentication, role management, and session propagation, you can focus on what matters: applying your business logic and protecting your data.
This model isn’t limited to logistics—the same principles apply to finance, healthcare, and any SaaS platform where contextual access decisions are critical.
Try the demo
Ready to implement your own RBAC + ABAC authorization model? We’ve got options for you:
- Check out the sample project
- Contact our sales team to learn more
- Get started with Frontegg for free today
The Complete Guide to SaaS Multi-Tenant Architecture
Read the guide