How Do You Model Permissions?

Has anyone found a good way to model permissions on a resource in JSON API?

I want to describe a Campaign resource that a User can or cannot take action on, such as applying to it.

Note that in our JSON API implementation, we’ve chosen to make all resources always include all of their attributes as a standard representation of that resource.

I can see a few different approaches: meta properties, related resource, other resource+user resource, and response header Allow values.

Meta Properties

This approach uses the meta member to describe the permissions the authenticated user has on the returned Campaign resource. The description for meta in the spec says that it can be used to include non-standard meta-information. These permission properties are not part of the standard representation of the resource, in my opinion. So, it makes sense that they could go in meta.

However, we then have to invent a permission structure that lives in meta. One approach may be the following:

GET /campaigns/1
{
  "id": 1,
  "type": "campaigns",
  "attributes": {
    // ...
  },
  "meta": {
    "permissions": {
      "apply": true
    }
  }
}

The client would then look in meta.permissions to determine if the current user could or could not take specific actions.

Related Resource

This approach creates a related resource to describe the permissions the authenticated user has on the returned Campaign resource. This other resource wouldn’t be something that could be directly CRUDed and would need some fake id property. Much like the meta approach, this provides a separate place to contain the non-standard information about a Campaign.

GET /campaigns/1
{
  "id": 1,
  "type": "campaigns",
  "attributes": {
    // ...
  },
  "relationships": {
    "campaign-user-permission": {
      "data": {
        "type": "campaign-user-permissions",
        "id": 2
      }
    }
  },
  "included": [
    {
      "type": "campaign-user-permissions",
      "id": 2,
      "attributes": {
        "canApply": true
      }
    }
  ]
}

This approach doesn’t seem to buy us much over the meta approach and has several problems.

Resource+User Resource

Maybe what we need is just a new resource that represents the combinations of a Campaign and the User requesting it. That could look like:

GET /user-campaigns/1
{
  "id": 1,
  "type": "user-campaigns",
  "attributes": {
    // ...
    canApply: true
  }
}

This approach puts the permissions in attributes of a different resource. It’s not the same resource as Campaign and therefore can have a different standard representation.

This feels a bit messy, though. Are we going to CRUD this resource? Possibly. If we want to say that a specific user can see a specific campaign, we may want to create a UserCampaign resource for that.

Response Header Allow

This approach uses the Allow response header (from the HTTP spec) to describe the permissions the authenticated user has on the returned Campaign resource. It can only list HTTP Method names, but the HTTP spec mentions that you can use non-standard method names in here.

GET /campaigns/1

Allow: APPLY
{
  "id": 1,
  "type": "campaigns",
  "attributes": {
    // ...
  }
}

This approach isn’t as flexible as others. It has to apply to the entire response. If there are ever degrees of parmission on an action, it would be hard to describe those with this method.

In general, I would consider a dual-approach for modeling permissions.

I would recommend modeling permissions that can be edited as resources in their own right. Their structure should align with how you store them in your server-side ORM / DB. Depending on the complexity of your permission model, a single permission resource might relate a user or group of users to a single campaign or group of campaigns.

With that said, clients should not necessarily need to “download the world” in order to understand how permissions apply to individual resources. Clients should easily be able to determine what they can do with an individual resource without understanding the reason why. For that reason, I would also recommend using meta as a side-channel to relay these applied permissions.

So to sum up, I think there can be value in presenting normalized permissions as editable resources, and there can also be value in de-normalizing those permissions as read-only meta-data related to a resource.

If the permission model ended up being complicated and editable, I like the idea of them being represented as resources and having (as you said) the applied permissions to a resource available in the meta section. If it’s a simple permission system that’s not really editable by clients, I think the meta approach makes sense on its own.

In the case where it starts simple and grows, given the above, we’d be using meta anyway. So, I think meta is the best approach.

1 Like