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 apply
ing 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.