Skip to content
Last updated

Relationship-Based Access Control in Frontegg

The ReBAC (Relationship-Based Access Control) feature in Frontegg is a flexible authorization system that allows you to define and manage access control based on the relationships between entities — such as users, files, and folders — and the actions that can be performed based on those relationships.


What is ReBAC in Frontegg?

ReBAC in Frontegg enables fine-grained authorization decisions by modeling entities, their relationships, and the actions they can perform. This model reflects real-world associations more naturally, especially in complex systems where permissions depend on who the user is related to, what resource they want to access, and how they’re connected.

Frontegg’s ReBAC lets you go beyond static roles by defining access permissions based on the relationship between a user and a specific resource.

Prerequisites

Before using ReBAC capabilities, make sure your environment is properly configured. ReBAC requires completing the initial setup steps described in the Entitlement Setup guide. If you are currently using the legacy Entitlements Agent, please refer to the Migration Guide.

Use cases

  • Ownership and role-based permissions grant elevated privileges (like edit or delete) to users with designated roles (e.g., owner, admin) over specific resources they control.
  • Hierarchical access inheritance enables permissions granted at a higher-level entity (like a tenant, group, or folder) to automatically apply to all nested resources, reducing the need for repetitive access assignments.
  • Access discovery and visibility allow applications to list all resources a user can access or all users who can access a resource using paginated queries that scale to large datasets.
  • Time-bound entitlements enforce access automatically based on validity periods, ensuring that temporary permissions, subscriptions, or trial access expire correctly without manual intervention.

How to use

Define your access model

You can define your access model (i.e. the schema) via the Frontegg portal UI or API.

Each operation described in the UI is mapped to the relevant APIs. To begin with model creation via API go to the API documentation.

To create it via Frontegg portal UI, navigate to [ENVIRONMENT] → Configurations → Entitlements → ReBAC in the Frontegg Portal.

For example, let’s say your app has documents, and users can be editors or viewers of specific documents. In this case, you'll need to create the user and document entity types, define the relations (viewer, editor), and map actions (read, write) to those relations.

  • An entity type: document
  • Relations: viewer, editor
  • Actions: read, write mapped to those relations

Follow the steps below to configure your model.

Create the user entity
  1. Go to the Entity tab.
  2. Click Create Entity.
  3. Set the Key to user and optionally add a description.
  4. Click Create.

This entity type will be used as the subject in your relationships.

To edit an entity later, click the three dots next to the entity and select Edit Entity to update attributes or change the configuration.

Create the document entity
  1. Go to the Entity tab.
  2. Click Create Entity.
  3. Set the Key to document and optionally add a description.
  4. Click Create.
Create a relation
  1. Go to the Entity tab.
  2. Click the three dots in the same row as the entity you want to edit, and select Edit entity.
  3. Go to the Relations tab.
  4. Click Create Relation.
  5. Enter the Relation Key (e.g., viewer, editor).
  6. Select user as the Subject Entity from the dropdown.
  7. Click Create.

You can delete or edit relations later from the same tab.

Create an action
  1. Go to the Entity tab.
  2. Click the three dots in the same row as the document entity, and select Edit entity.
  3. Go to the Actions tab.
  4. Click Create Action.
  5. Enter the Action Key (e.g., read, write).
  6. Select the Relations that grant this action from the dropdown.
  7. Click Create.

You can edit or delete actions later if needed.

Assign relationships between entities

Now that your model is defined, you assign direct relations between your entities using API calls. To do this:

  1. Go to the Associations tab.
  2. Click Create Association.
  3. Select the Target Entity (e.g., document).
  4. Enter the Target ID (e.g., doc_456).
  5. Select the Relation (e.g., editor).
  6. Select the Subject Entity (e.g., user).
  7. Enter the Subject ID (e.g., user_123).
  8. Click Create to establish the relationship.

Alternatively, you can assign relationships by calling the POST /resources/relations/v1/assign endpoint with the following payload:


{
  "assignments": [
    {
      "subjectEntityTypeKey": "user",             // The type of the subject (e.g., user, group)
      "subjectKey": "anthony@email-of.his",       // The unique identifier of the subject
      "relationKey": "reader",                    // The name of the relation to assign
      "targetEntityTypeKey": "document",          // The type of the target resource
      "targetKey": "document-1.doc"               // The unique identifier of the target
    }
  ]
}

This API call creates a direct relationship between the specified subject and target. If the relation already exists, it will not be duplicated.

To remove a relation, use the POST /resources/relations/v1/unassign endpoint with the same payload structure.

Hierarchies

Hierarchical access allows a permission on a child entity (for example, a document) to be inherited from a related parent entity (such as a folder). This is done by composing actions across relationships, so that access granted on the parent automatically applies to its children.

Defining hierarchical access

Defining hierarchical access is currently supported only via the API and is not available in the Frontegg portal UI.


To create hierarchical access, define an action that combines:

  • A direct relation (for example, reader), and
  • An inherited relation path that references another action on a related entity (for example, parent → read_folde).

This tells the authorization engine that a user can perform an action either directly on the resource or indirectly through a parent relationship.

For example:

Create a read_doc action on the document entity that allows access if the user:

  • Is a direct reader of the document, or
  • Can perform the read_folder action on the document’s parent folder.

You can do this by calling the POST /entitlements/resources/entity-types/v1/document/actions/{actionKey} endpoint with the following:


{
  "relationKeys": [
    "reader",
    {
      "fromRelation": "parent",
      "toAction": "read_folder"
    }
  ]
}

This configuration enables hierarchical authorization, where permissions granted at the folder level automatically apply to documents within that folder.

Enforce access in your app

After setting up your entities, relations, actions, and assigning the appropriate relationships, the next step is to enforce access control inside your application. This is typically done during request handling or in middleware layers by checking whether a user is allowed to perform a given action on a resource.

Using the SDK

You use the @frontegg/e10s-client SDK to send an authorization check to the Entitlements engine, which makes the final decision based on the ReBAC configuration. The client also provides lookup operations to query the ReBAC authorization model and retrieve paginated lists of access relationships between entities. To use the SDK, you must have the Entitlements engine properly set up and running in your environment. Go to Entitlement Setup documentation and follow the instructions.

First, install the SDK:


npm install @frontegg/e10s-client

Next, initialize the client:


import { EntitlementsClientFactory, RequestContextType } from '@frontegg/e10s-client';

const e10sClient = EntitlementsClientFactory.create({
	engineEndpoint: 'localhost:50051',
	engineToken: 'your-engine-token'
});

Then, at the point in your application where access needs to be checked:


const entitlementsCheck = await e10sClient.isEntitledTo(
	{ entityType: 'user', key: 'user_123' },
	{
		type: RequestContextType.Entity,
		entityType: 'document',
		key: 'doc_456',
		action: 'read',
	},
);

And handle the result:


if (!entitlementsCheck.result) {
  return res.status(403).json({
    message: 'Access denied',
   });
}

Lookup Target Entities:

Find all TargetEntity instances (i.e. documents) of a given type that a entity (i.e user) is entitled to perform a specific action on. Use cursor for pagination if required.


const response = await e10sClient.lookupTargetEntites({
	entityType: 'user',
	entityId: 'user-123',
	TatgetEntityType: 'document',
	action: 'read',
	limit: 100, // Optional: limit number of results (default: 50, max: 1000)
	cursor: undefined // Optional: pagination cursor
});

console.log(`Found ${response.totalReturned} Target Entites`);

response.targets.forEach((target) => {
	console.log(`${target.TatgetEntityType}:${target.TargetEntityId}`);
	// resource.permissionship: 'HAS_PERMISSION' | 'CONDITIONAL_PERMISSION' | 'NO_PERMISSION'
});

// For pagination, use the returned cursor
if (response.cursor) {
	const nextPage = await e10sClient.lookupResources({
		// ... same params
		cursor: response.cursor
	});
}

Lookup Entities:

Find all entities (i.e. users) of a given type that are entitled to perform a specific action on a given entity instance (i.e. documents)


const response = await e10sClient.lookupEntities({
	TatgetEntityType: 'document',
	TargetEntityId: 'doc-456',
	entityType: 'user',
	action: 'read'
});

console.log(`Found ${response.totalReturned} entities`);

response.entities.forEach((entity) => {
	console.log(`${entity.entityType}:${entity.entityId}`);
	// subject.permissionship: 'HAS_PERMISSION' | 'CONDITIONAL_PERMISSION' | 'NO_PERMISSION'
});

Time-based access (active_at caveat):

Time-based relations let you grant access that is only active at a specific time (e.g., trials, subscription periods, temporary access). For relationships that use the active_at caveat to control time-based access, you can specify the at parameter to evaluate access at a specific point in time. The at parameter is also supported in lookup operations with same format and behavior.

Note

The at parameter accepts ISO 8601 format strings:

  • UTC format: 2025-12-31T23:59:59Z
  • Timezone offset: 2025-12-31T23:59:59+02:00 (If at not provided, it defaults to the current UTC time).

const e10sResult = await e10sClient.isEntitledTo(
	{
		entityType: 'user',
		key: 'some@user.com'
	},
	{
		type: RequestContextType.Entity,
		entityType: 'document',
		key: 'README.md',
		action: 'read',
		at: '2026-01-15T12:00:00Z'
	}
);

if (!e10sResult.result) {
	console.log(`User is not allowed to read document at the specified time`);
}