Skip to main content

Authorization Through Organization Context

note
Auth0 Fine Grained Authorization (FGA) is the early-stage product we are building at Auth0 to solve fine-grained authorization at scale. Sign up for the Developer Community Preview to try it out, and join our Discord community if you are interested in learning more about our plans.

Please note that at this point in time, it is not considered production-ready and does not come with any SLAs; availability and uptime are not guaranteed. Limitations of Auth0 FGA during the Developer Community Preview can be found here.

This section tackles cases where a user may have access to a particular resource through their presence in a particular organization, and they should have that access only when logged in within the context of that organization.

When to use
Contextual Tuples should be used when modeling cases where a user's access to an object depends on the context of their request. For example:
  • An employee’s ability to access a document when they are connected to the organization VPN or the api call is originating from an internal IP address.
  • A support engineer is only able to access a user's account during office hours.
  • If a user belongs to multiple organizations, they are only able to access a resource if they set a specific organization in their current context.

Before you start

To follow this guide, you should be familiar with some Auth0 FGA Concepts.

You also need to be familiar with:

  • Modeling Object-to-Object Relationships: You need to know how to create relationships between objects and how that might affect a user's relationships to those objects. Learn more →
  • Modeling Multiple Restrictions: You need to know how to model requiring multiple authorizations before allowing users to perform certain actions. Learn more →

The Playground

Try this guide out on the Auth0 FGA Playground

Scenario

For the scope of this guide, we are going to consider the following scenario.

Consider you are building the authorization model for a multi-tenant project management system.

In this particular system,

  • projects are owned and managed by companies
  • users can be members of multiple companies
  • project access is governed by the user's role in the organization that manages the project

In order for a user to access a project:

  • The project needs to be managed by an organization the user is a member of
  • A project is owned by a single organization
  • A project can be shared with partner companies (that are able to view, edit but not perform admin actions, such as deletion, on the project)
  • The user should have a role that grants access to the project
  • The user should be logged in within the context of that organization

We will start with the following authorization model:

type organization
relations
define member as self
define project_manager as self
define project_editor as self
type project
relations
define owner as self
define partner as self
define manager as project_manager from owner
define editor as project_editor from owner or project_editor from partner or manager
define can_delete as manager
define can_edit as editor
define can_view as editor
We are considering the case that: - Anne has a project manager role at organizations A, B and C - Beth has a project manager role at organization B - Carl has a project manager role at organization C - Project X is owned by organization A - Project X is shared with organization B

The above state translates to the following relationship tuples:


await fgaClient.write({
writes: {
tuple_keys: [
// Anne has a `project manager` role at organization A
{ user: 'anne', relation: 'project_manager', object: 'organization:A'},
// Anne has a `project manager` role at organization B
{ user: 'anne', relation: 'project_manager', object: 'organization:B'},
// Anne has a `project manager` role at organization C
{ user: 'anne', relation: 'project_manager', object: 'organization:C'},
// Beth has a `project manager` role at organization B
{ user: 'anne', relation: 'project_manager', object: 'organization:B'},
// Carl has a `project manager` role at organization C
{ user: 'carl', relation: 'project_manager', object: 'organization:C'},
// Organization A owns Project X
{ user: 'organization:A', relation: 'owner', object: 'project:X'},
// Project X is shared with Organization B
{ user: 'organization:B', relation: 'partner', object: 'project:X'}
]
}
});

Requirements

  • When logging in within the context of organization A, Anne should be able to view and delete project X.
  • When logging in within the context of organization B, Anne should be able to view, but not delete, project X.
  • When logging in within the context of organization C, Anne should not be able to view nor delete project X.
  • When logging in within the context of organization B, Beth should be able to view, but not delete, project X.
  • Carl should not be able to view nor delete project X

Step by Step

In order to solve for the requirements above, we will break the problem down into three steps:

  1. Understand the authorization checks can be done with the existing model
  • Ensure that Anne can view and delete "Project X"
  1. Extend the authorization model to take time and ip address into consideration
  2. Use contextual tuples for context related checks

Checking Access (Excluding Contextual Data)

With the authorization model and relationship tuples shown above, Auth0 FGA has all the information needed to ensure that Anne can view and delete "Project X".

We can verify that using the following checks:

  • Anne can view Project X

    // Run a check
    const { allowed } = await fgaClient.check({
    tuple_key: {
    user: 'anne',
    relation: 'can_view',
    object: 'project:X',
    },});

    // allowed = true
More checks
* Anne can delete Project X

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'anne',
relation: 'can_delete',
object: 'project:X',
},});

// allowed = true
* Beth can view Project X

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'beth',
relation: 'can_view',
object: 'project:X',
},});

// allowed = true
* Beth cannot delete Project X

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'beth',
relation: 'can_delete',
object: 'project:X',
},});

// allowed = false
* Carl cannot view Project X

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'carl',
relation: 'can_view',
object: 'project:X',
},});

// allowed = false
* Carl cannot delete Project X

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'carl',
relation: 'can_delete',
object: 'project:X',
},});

// allowed = false

Note that so far, we have not prevented Anne from viewing "Project X" even if Anne is viewing it from the context of Organization C.

Take Organization Context into Consideration

Extend the Authorization Model

In order to add a restriction based on the current organization context, we will make use of Auth0 FGA configuration language's support for intersection to specify that a user has to both have access and be in the correct context in order to be authorized.

We can do that by introducing some new relations and updating existing relation definitions:

  1. On the "organization" type
  • Add "user_in_context" relation to mark that a user's access is being evaluated within that particular context
  • Update the "project_manager" relation to require that the user be in the correct context (by adding and user_in_context to the relation definition)
  • Considering that Auth0 FGA does not yet support multiple logical operations within the same definition, we will split "project_editor" into two:
    • "base_project_editor" editor which will contain the original relation definition (self or project_manager)
    • "project_editor" which will require that a user has both the "base_project_editor" and the "user_in_context" relations

The "organization" type definition then becomes:

type organization
relations
define member as self
define project_manager as self and user_in_context
define base_project_editor as self or project_manager
define project_editor as base_project_editor and user_in_context
define user_in_context as self
note

On the "project" type:

  • Nothing will need to be done, as it will inherit the updated "project_manager" and "project_editor" relation definitions from "organization"
Add the required tuples to mark that anne is in an approved context

Now that we have updated our authorization model to take the current user's organization context into consideration, you will notice that anne has lost access because nothing indicates that anne is authorizing from the context of an organization. You can verify that by issuing the following check:


// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'anne',
relation: 'can_view',
object: 'project:X',
},});

// allowed = false

In order for Anne to be authorized a tuple indicating Anne's current organization context will need to be present:

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

await fgaClient.write({
writes: {
tuple_keys: [
// Anne is authorizing from the context of organization:A
{ user: 'anne', relation: 'user_in_context', object: 'organization:A'}
]
}
});

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'anne',
relation: 'can_view',
object: 'project:X',
},});

// allowed = true

Using contextual tuples

Now that we know we can authorize based on present state, we have a different problem to solve. We are storing the tuples in the state in order for Auth0 FGA to evaluate them, which fails in certain use-cases where Anne can be connected to two different contexts in different browser windows at the same time, as each has a different context at the same time, so if they are written to the state, which will Auth0 FGA use to compute Anne's access to the project?

For Check calls, Auth0 FGA has a concept called "Contextual Tuples". Contextual Tuples are tuples that do not exist in the system state and are not written beforehand to Auth0 FGA. They are tuples that are sent alongside the Check request and will be treated as if they already exist in the state for the context of that particular Check call. That means that Anne can be using two different sessions, each within a different organization context, and Auth0 FGA will correctly respond to each one with the correct authorization decision.

When Anne is connecting from the context of organization A, Auth0 FGA will return {"allowed":true}:


// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'anne',
relation: 'can_view',
object: 'project:X',
},
contextual_tuples: {
tuple_keys: [
{
user: "anne",
relation: "user_in_context",
object: "organization:A"
}
]
}});

// allowed = true

When Anne is connecting from the context of organization C, Auth0 FGA will return {"allowed":false}:


// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'anne',
relation: 'can_view',
object: 'project:X',
},
contextual_tuples: {
tuple_keys: [
{
user: "anne",
relation: "user_in_context",
object: "organization:C"
}
]
}});

// allowed = false

Using this, you can check that the following requirements are satisfied:

UserOrganization ContextActionAllowed
AnneOrganization AViewYes
AnneOrganization BViewYes
AnneOrganization CViewYes
AnneOrganization ADeleteYes
AnneOrganization BDeleteNo
AnneOrganization CDeleteNo
BethOrganization BViewYes
BethOrganization BDeleteNo
CarlOrganization CViewNo
CarlOrganization CDeleteNo

Summary

Final version of the Authorization Model and Relationship tuples
type organization
relations
define member as self
define project_manager as self and user_in_context
define base_project_editor as self or project_manager
define project_editor as base_project_editor and user_in_context
define user_in_context as self
type project
relations
define owner as self
define partner as self
define manager as project_manager from owner
define editor as manager or project_editor from owner or project_editor from partner
define can_delete as manager
define can_edit as editor
define can_view as editor

await fgaClient.write({
writes: {
tuple_keys: [
// Anne has a `project manager` role at organization A
{ user: 'anne', relation: 'project_manager', object: 'organization:A'},
// Anne has a `project manager` role at organization B
{ user: 'anne', relation: 'project_manager', object: 'organization:B'},
// Anne has a `project manager` role at organization C
{ user: 'anne', relation: 'project_manager', object: 'organization:C'},
// Beth has a `project manager` role at organization B
{ user: 'beth', relation: 'project_manager', object: 'organization:B'},
// Carl has a `project manager` role at organization C
{ user: 'carl', relation: 'project_manager', object: 'organization:C'},
// Organization A owns Project X
{ user: 'organization:A', relation: 'owner', object: 'project:X'},
// Project X is shared with Organization B
{ user: 'organization:B', relation: 'partner', object: 'project:X'}
]
}
});
Warning

Contextual tuples:

  • Are not persisted in the store.
  • Are only supported on the Check API endpoint. They are not supported on read, expand and other endpoints.
  • If you are using the Read Changes API endpoint to build a permission aware search index, note that it will not be trivial to take contextual tuples into account.
Modeling with Multiple Restrictions

Learn how to model requiring multiple relationships before users are authorized to perform certain actions.

Contextual and Time-Based Authorization

Learn how to authorize access that depends on dynamic or contextual criteria.

Auth0 FGA Check API

Details on the Check API in the Auth0 FGA reference guide.

Have Feedback?

Join us on the Discord community if you have any questions or suggestions.