Skip to main content

Modeling Parent-Child Objects

In this guide you will learn how to indicate relationships between objects, and how users' relationships to one object can affect their relationship with another. For example: how a editor of a folder can be an editor of all documents the folder is a parent of.

When to use

Object-to-Object relationships, combined with a configured authorization model can be used to indicate that a user's relation with one object can influence their relationship with another object. This can also be used in some cases to remove the need to modify relations between objects using user groups.

Object-to-object relationships can be used:

  • To indicate that managers of an employee have access to approve requests the employee has made
  • To indicate that users who have a repository admin role (repo_admin) in an organization automatically have admin access to all repositories in that organization
  • To indicate that users who are subscribed to a plan, get access to all the features in that plan

Before You Start

In order to understand this guide correctly you must be familiar with some Okta FGA Concepts and know how to develop the things that we will list below.

Assume that you have the following authorization model.
You have two types:

  • folder that users can be related to as an editor
  • document that users can be related to as an editor
model
schema 1.1

type user

type folder
relations
define editor: [user]

type document
relations
define editor: [user]

In addition, you will need to know the following:

Direct Access

You need to know how to create an authorization model and create a relationship tuple to grant a user access to an object. Learn more →

Okta FGA Concepts

  • A Type: a class of objects that have similar characteristics
  • A User: an entity in the system that can be related to an object
  • A Relation: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
  • An Object: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
  • A Relationship Tuple: a grouping consisting of a user, a relation and an object stored in Okta FGA
  • Union Operator: the union operator can be used to indicate that the user has multiple ways of being related to an object

The Playground

Try this guide out on the Okta FGA Playground

Step By Step

We would like to model folders that contain documents and that a user having editor access to the folder, has editor access to all documents in that folder.

For editors of a folder to be editors of a containing document, we need to:

  1. Update the authorization model to allow a parent relationship between folder and document
  2. Update the editor relation in the document type definition to support cascading from folder

and then to indicate and verify that bob is an editor of document:meeting_notes.doc because bob is an editor of folder:notes, we then need to:

  1. Create a new relationship tuple to indicate that bob is a editor of folder:notes
  2. Create a new relationship tuple to indicate that folder:notes is a parent of document:meeting_notes.doc
  3. Check to see if bob is an editor of document:meeting_notes.doc

01. Update The Authorization Model To Allow A Parent Relationship Between Folder And Document

To allow a parent relation between a folder and a document, we need to update the authorization model to be the following (this is to allow creating relationship tuples that can indicate a relationship between folder and document. Refer to Modeling Concepts: Object to Object Relationships to understand why):

model
schema 1.1

type user

type folder
relations
define editor: [user]

type document
relations
define parent: [folder]
define editor: [user]
info

Notice that the document type now has a parent relation, indicating that other objects can be parents of documents

02. Update The Editor Relation In The Document Type Definition To Support Cascading From Folder

To allow cascading relations between folder and document, we can update our authorization model as the following:

model
schema 1.1

type user

type folder
relations
define editor: [user]

type document
relations
define parent: [folder]
define editor: [user] or editor from parent
info

editor of a document are any of:

  1. users that are directly assigned as editors
  2. users that are related to any parent of this document as editor (editors of the parent)

So with this change, anyone related to a folder that is a parent of a document as an editor is also an editor of said document.

03. Create A New Relationship Tuple To Indicate That bob Is An Editor Of folder:notes

To leverage our cascading relation, we need to create a relationship tuple that states: bob is an editor of folder:notes

Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';

// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'fga.us.auth0.com'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page

const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});

await fgaClient.write({
writes: [
{"user":"user:bob","relation":"editor","object":"folder:notes"}
],
}, {
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw"
});
caution

Note: Make sure to use unique ids for each object and user within your application domain when creating relationship tuples for Okta Fine Grained Authorization (FGA). We are using first names and simple ids to illustrate an easy-to-follow example.

04. Create A New Relationship Tuple To Indicate That folder:notes Is A Parent Of document:meeting_notes.doc

Now that bob is an editor of folder:notes, we need to indicate that folder:notes is a parent of document:meeting_notes.doc

Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';

// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'fga.us.auth0.com'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page

const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});

await fgaClient.write({
writes: [
// the notes folder is a parent of the meeting notes document
{"_description":"the notes folder is a parent of the meeting notes document","user":"folder:notes","relation":"parent","object":"document:meeting_notes.doc"}
],
}, {
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw"
});

05. Check To See If bob Is An Editor Of document:meeting_notes.doc

Now that we have done the changes to the authorization model and the addition of the two new relationship tuples, we can verify if our configuration is correct by running the following check: is bob an editor of document:meeting_notes.doc.

Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';

// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'fga.us.auth0.com'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page

const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});

// Run a check
const { allowed } = await fgaClient.check({
user: 'user:bob',
relation: 'editor',
object: 'document:meeting_notes.doc',
}, {
authorization_model_id: '1uHxCSuTP0VKPYSnkq1pbb1jeZw',
});

// allowed = true

Note: There are no other relationship tuples within the store that dictate any direct relation between bob and document:meeting_notes.doc. The check succeeds because of the cascading relation applied.

The chain of resolution becomes:

  • bob is an editor of folder:notes
  • folder:notes is a parent of document:meeting_notes.doc
  • editors of any parent folder of document:meeting_notes.doc are also editors of the document
  • Therefore, bob is an editor of document:meeting_notes.doc
caution

Note that when searching tuples that are related to the object (the word after from, also called the tupleset), Okta Fine Grained Authorization (FGA) will not do any evaluation and will only consider concrete objects (of the form <object_type>:<object_id>) that were directly assigned, and will throw an error if it encounters any rewrites, or a *, a type bound public access (<object_type>:*) or a userset (<object_type>:<object_id>#<relation>).

See Referencing Relations on Related Objects for more.

Modeling Concepts: Object to Object Relationships

Learn about how to model object to object relationships in Okta FGA.

Modeling Google Drive

See how to make folders parents of documents, and to make editors on the parent folders editors on documents inside them..

Modeling GitHub

See how to grant users access to all repositories owned by an organization.

Have Feedback?

You can use any of our support channels for any questions or suggestions you may have.