Skip to main content

Building multi-tenant applications

A multi-tenant application is a software application designed to be used and shared by multiple users or user groups, referred to as "tenants". These tenants can be organizations, business units, teams, or even individual users, each with their own data and configuration. Every B2B SaaS product or consumer app is a multi-tenant application.

Multi-tenant applications require additional access control capabilities because of two unique requirements:

  • The tenants must only be able to access their own data.
  • The application must support potentially millions of unique tenants.

Creating individual Access Policies for each tenant would result in a large development overhead. You would need to manage the lifecycle of potentially millions of Access Policies, along with the lifecycle of the tenant in your application. Furthermore, updating a policy to include a new column, for example, would require updating thousands or millions of Access Policies (one per user or customer).

info

We recommend against creating an Access Policy per customer for multi-tenant applications.

Instead, you can create a single Access Policy for all your customers with a dynamic, row-level rule. Dynamic, row-level rules allow you to evaluate the Access Policy against values encoded in your Application’s access token.

Example​

The following shows the Console and JSON representations of an Access Policy for TacoSoft, our demo SaaS application. Notice how the Policy filters on “restaurant_name”. We do this because each restaurant is its own tenant and should only have access to its own records. Also, notice how we filter for ${{ restaurant_name }}. This is a placeholder that means, “get this value from the Application’s access token.”

{
"id": "POL00000000000000000000000000",
"uniqueName": "Multi-tenant Restaurant Policy",
"description": "Limit access to a restaurant, based on the access token",
"columns": ["*"],
"rows": [
{
"column": "restaurant_name",
"operator": "EQUALS",
"value": "${{ restaurant_name }}"
}
],
"dataPool": {
"id": "DPO11111111111111111111111111"
}
}

Before we can use this Policy, we need to change the way we create access tokens in our app. The Policy depends on a dynamic value ${{ restaurant_name }}, and so we need to set this when creating our access token, according to the currently logged-in restaurant operator. Then, we can assign the Policy to our Application, and Propel will start enforcing our dynamic, row-level rule.

Let’s see how this works step-by-step:

sequenceDiagram App->>+Propel Token API: 1. Create an access token with {"restaurant_name":"Farolito"} Propel Token API->>-App: 2. Receive an access token including {"restaurant_name":"Farolito"} App->>+Propel GraphQL API: 3. Query a Data Pool or Metric using the access token Propel GraphQL API->>Propel GraphQL API: 4. Evaluate Access Policy using<br>{"restaurant_name":"Farolito"} Propel GraphQL API->>-App: 5. Receive the data filtered by restaurant_name = Farolito
  1. Our app creates an access token using Propel’s Token API and its Propel Application’s client ID and secret. It also sets “restaurant_name” equal to “Farolito”.
  2. When creating the access token, Propel’s Token API encodes the “restaurant_name” into the token and cryptographically signs it.
  3. Our app then queries a Data Pool or Metric using Propel’s GraphQL API, passing the access token in the Authorization header.
  4. Propel’s GraphQL API evaluates the Data Pool’s Access Policy for the Application, substituting the placeholder ${{ restaurant_name }} for the value in the token. This results in filtering for restaurant_name = "Farolito" before executing the query.
  5. Propel’s GraphQL API executes the query and returns the data, filtered for restaurant_name = "Farolito".

Creating an access token with dynamic values​

The example below uses curl to generate an access token for a Propel Application with the dynamic value “restaurant_name” set to “Farolito”.

curl https://auth.us-east-2.propeldata.com/oauth2/token \
-d grant_type=client_credentials \
-d client_id=$APPLICATION_ID \
-d client_secret=$APPLICATION_SECRET \
-d 'policy_values={"restaurant_name":"Farolito"}'

Creating the access token should always be performed from secure, server-side code; however, the resulting access token is both time-bound and tenant-bound (assuming proper Access Policies are in place). This makes it safe to include the access token in frontend code when calling Propel’s GraphQL API.

Propel does not have an opinion on how your application does multi-tenancy nor if it has multiple levels of multi-tenancy (users, customers, organizations, etc.). You can pass any values you need dynamically and create Access Policies to enforce them.