7

What is the best practice for exposing different fields for a resource based on user's role/ACL privileges in the system?

Let's say I have an endpoint, groups/{:group_id}. My business rules state the following:

  1. Non-authenticated users can see only: name and ID fields
  2. Authenticated users can see: name, ID, managers, and access_code fields
  3. Group managers can see name, ID, managers, access_code and members.

Respectively, the records may look like:

/* Non-Authenticated User */
{
    "id": "abe80d",
    "name": "Foobar Group"
}

/* Authenticated User */
{
    "id": "abe80d",
    "name": "Foobar Group",
    "access_code": "abc123",
    "managers": {
        "_href": "http://example.org/people/bds983a",
        "_href": "http://example.org/people/cde03rf",
    }
}

/* Manager */
{
    "id": "abe80d",
    "name": "Foobar Group",
    "access_code": "abc123",
    "managers": {
        "_href": "http://example.org/people/bds983a",
        "_href": "http://example.org/people/cde03rf",
    },
    "members": {
        "_href": "http://example.org/people/bds983a",
        "_href": "http://example.org/people/cde03rf",
        "_href": "http://example.org/people/jvs239a",
        "_href": "http://example.org/people/nnd9323",
    }
}

Should this be a single endpoint/URI that shows different sets of fields for different authentication levels, or should I somehow break these up into three different URIs?

It seems like different URLs for the same resource is bad, but a client not knowing exactly what fields would be retrieved is also bad? Is there a best practice in this situation?

4

Usually a single resource URI because you want separation of concerns between WHERE the resource lives and WHO accesses it.

In your case, "WHO" accesses the resource is determined based on two parameters: Is the user authenticated? If so, what is the level of authorization?

For the level of authorization, you may implement it using groups, and may add groups over time, or change the authorization level of various groups over time. That should not change where the data needs to be retrieved from by a client.

A practical example is that when a client (e.g. a native app on a mobile device) consumes your API, it will authenticate the user and render the view based on the data it gets back. If (on the server side), new groups are added or a user's authorization level is changed, it should not have any impact on the client code.

It seems like different URLs for the same resource is bad, but a client not knowing exactly what fields would be retrieved is also bad? Is there a best practice in this situation?

Clients are generally expected to handle these cases. If you had separate URIs, you still have the problem that a URI is being hit which requires higher privileges than what the user has, in which case, the request will have to just fail. More importantly, without authentication having been performed, how does a client even know which URL to hit?

On the other hand, if the client was coded to expect data according to various authorization levels, the code would be generic enough to handle future cases where what data is returned is changed. This is important because a resource should generally not be assumed to stay constant and not evolve over time.

There are fortunately many tools and techniques available both in XML and JSON to handle arbitrary amount of data returned from the server so long as the schema contract was not violated.

In the example, you provided, I would code my client to have the schema for all five fields, but expect any field other than name and ID to be null. If it's not null, render it to the user, but if it is, either don't show it, or provide an appropriate message. In the future, if you decided to allow authenticated users to not see access_code or non-authenticated users to see managers, the client code would not have to change.

  • Thanks Omer, I've been leaning this way too. It seems like this will necessitate many conditional statements on the client, but that is an okay trade-off. Another thought that I had was to use different vendor MIME-types for different sets of fields (e.g. application/vnd.myapp.basic+json), but that smells bad to me. – caseyamcl Nov 21 '14 at 13:26
  • You're welcome. Even if you have multiple cases, it is often possible to keep the code clean (and reduce conditionals). Without knowing exactly what you're doing, the discussion is hard to have though. If you want, you can start a new question and it may solicit responses from other experts too. Btw, we all learn from others too... – Omer Iqbal Nov 22 '14 at 4:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.