Multitenant architecture comparison

How to secure a multitenant application architecture

Warren Parad

Published on July 12, 2020

A Multitenant application

Multitenancy is the concept that your application serves distinct non-overlapping accounts, with resources assigned to and belonging to each account. A simple example of this is an off-line console video game. Each game copy is bought and paid for by a single owner (“account”), and in that copy there may be some amount of gameplay saved data, development data, and user configuration which is unique and sequestered to that copy. In some cases that data may need to be synced with other copies that the user owns. For example a PC version or potentially a status update on their mobile device.

On the opposite side of the spectrum there are open communities where no data is restricted to the users, a simplified version of Twitter is a good example. Tweets are public, anyone can read them. While a user may be able to post new tweets and delete ones they posted, the environment that user sees is a combination of all users on that platform.

In these extremes, access control policies aren’t that meaningful, since users either have access to nothing (except their resources) or everything. It becomes required and thus more challenging, when resources are partially shared. Take a photo sharing application. While photos may be owned by a single account, a user may want to share their photos with their friends. Additionally they could create albums which their whole family can see, or public ones they intend to demonstrate their photography prowess.

Users create separate accounts which have their own data, own configuration, but may need to share that data with users in that account, or users in other accounts. This is at the core of what is considered a multitenant application.

Application resources

If we use a document repository example we can start to build out what the recommendations might be to secure that data. To start off we’ll look at the types of actions users may want to perform, these are often called user stories.

This is just some of the functionality necessary to be built into a document repository. While it is trivial to save documents (depending on their size), getting the permissions right so that they are both easy to maintain but can also provide the necessary flexibility for your users to control access they want to is difficult. We’ll walk through one possible implementation below.

Modeled Actions

  1. Create Account - Let’s assume no restricted access is necessary here, anyone can create an account.
  2. Invite User to Account - Perhaps something like
    1. Resource: accounts/{accountId}/users
    2. Permissions: AddUser/InviteUser, RemoveUser, ReadUsers
  3. List accounts - I’ll assume accounts are private. A user can only list accounts they are part of, potentially if you share a document a user might need to be able to see some aspects about that account that owns the document:
    1. Resource: accounts/{accountId} and accounts/{accountId}/info
    2. Permissions: updateName/Description, ReadAccount
  4. List documents - Need to be able to list the documents in the account
    1. Resource: accounts/{accountId}/documents/(documentPath)
    2. Permissions: AddDocument, DeleteDocument, ReadDocument, EditDocument
  5. List users - Need to be able to list users that have access to a document
    1. Resource: accounts/{accountId}/documents/(documentPath)/members
    2. Permissions: ShareDocument, RemoveAccess, UpdateAccess, AssignDocumentOwner

These resource uris are a good match for our user stories about a document repository. For these we would create the relevant roles. So far we listed out the permissions, since the permissions are checked by services we’ll want role abstractions that contain these permissions for the resources.

Relevant Roles

Relevant Resources

Standard Multitenant Resource Recommendations

It can be difficult to get the resource paths just right for your application. What’s important is matching up the user stories to necessary access control checks. Since Authress provides scoped permissions and resources, the best recommendation are resource uris that looks similar to the following:

NS:tenants/{tenantId}/parentResources/{parentResourceId}/resources/{resourceId}/subResources/{subResourceId}

(Another example exists in the Zoom Case study)

In some cases resources are very fluid and too much scoping can be a problem, but in Authress these can be changed by updating access records and a simple migration can be used to propagate them if the access control model needs to be changed.

Since you're here, check out what Authress is all about!

Enjoyed reading this article? There's more in our Knowledge Base.