Skip to main content

Conditional Relationship Tuples for OpenFGA

· 5 min read
Andres Aguiar
Product Manager

Relationship Tuples are the facts that the OpenFGA evaluates to determine whether a user is permitted to access a resource.

The way tuples are considered when making authorization decisions in OpenFGA is guided by an authorization model, which employs concepts from Relationship-Based Access Control (ReBAC) to establish authorization policies. For instance, you might declare that users are allowed to view a document if they have permission to view its parent folder.

Although ReBAC offers a highly flexible method for structuring permissions, it encounters difficulties with defining permissions based on attributes that are not easily represented as relationships. Attributes such as “parent folder,” “department,” “region,” and “country” can be conceptualized as relationships between two entities. However, attributes like “IP address,” “time of day,” “team size limit,” or “maximum amount for a bank transfer” cannot be easily handled.

In our ongoing efforts to expand OpenFGA’s capacity for articulating a broader range of authorization policies, we are introducing Conditional Relationship Tuples. These allow for the specification of conditions under which a particular tuple is relevant when evaluating an authorization query.

Consider the following example, where we utilize Conditional Tuples to grant access for a user over a specified time duration. We stipulate that a user may be granted either unconditional access or access constrained to a certain time period:

model
schema 1.1

type user

type document
relations
define viewer: [user, user with non_expired_grant]

condition non_expired_grant(current_time: timestamp, grant_time: timestamp, grant_duration: duration) {
current_time < grant_time + grant_duration
}

If we write the following tuples:

userrelationobjectcondition
user:bobviewerdocument:1
user:anneviewerdocument:1name : non_expired_grant, context : { grant_time : 2023-01-01T00:00:00Z, grant_duration : 1h }

You'll get the following results for the Check operations below:

userrelationobjectcontextresult
user:bobviewerdocument:1allowed : true
user:anneviewerdocument:1current_time : 2023-01-01T00:10:00Zallowed : true
user:anneviewerdocument:1current_time : 2023-01-01T02:00:00Zallowed : false
user:anneviewerdocument:1error : "failed to evaluate relationship condition 'non_expired_grant': context is missing parameters '[current_time]'

You'll get the following results for the ListObjects operations below:

userrelationobjectcontextresult
user:anneviewerdocument:1current_time : 2023-01-01T00:10:00Zobjects: [ "document:1"]
user:anneviewerdocument:1error: "failed to evaluate relationship condition 'non_expired_grant': tuple 'document:1#viewer@user:anne' is missing context parameters '[current_time]'

Note that:

  • user:bob will always get allowed:true as we have assigned as viewer unconditionally.
  • user:anne will get allowed:true if the current_time is before the grant_time + grant_duration and allowed:false otherwise.
  • If you don't provide the current_time in the context, the Check and ListObjects operations will fail.

Use Cases

The OpenFGA Sample Stores repository has several examples that take advantage of this new feature:

How to use it?

Conditional Relationship Tuples are included in OpenFGA 1.4.0-rc1 version. You can run it by pulling it from docker:

docker pull openfga/openfga:v1.4.0-rc1
docker run -p 8080:8080 -p 8081:8081 -p 3000:3000 openfga/openfga:v1.4.0-rc1 run`

OpenFGA has a rich ecosystem of developer tools. The following have been updated to support Conditional Relationship Tuples:

What’s Next?

We’ll address some limitations of the current implementation:

  • The Expand API does not consider conditions.
  • The Visual Studio Code integration is not validating the expressions in conditions.
  • The Playground does not let you add context for tuples and assertions. You should use the VS Code Extension + the FGA CLI to test your models for now.

We'll also improve ListObjects scenarios when it's called with missing context. For example, consider the following model that enables access only to documents with a specific status:

model
schema 1.1

type user

type document
relations
define can_access: [user with docs_in_draft_status]

condition docs_in_draft_status(status: string) {
status == "draft"
}

If you want to list all the documents a user can view, you'll need to know the status of all of those documents. Given you don't know the documents the user has access too, you can't send the status of those as a parameter to ListObjects.

Our goal is to return a structure that you can use to filter documents on your side, similar to: (document.id = ‘1’ and document.status = ‘draft’) or (document.id = ‘2’ and.status = draft)
This won’t scale to a large number of documents, but would be useful in some scenarios.

Reach out!

We want to learn how you use this feature and how we can improve it!

Please reach out through our community channels with any questions or feedback.