For role-based access to a RESTful API, is it common practice (and considered safe) to store access permissions in a database table? I'm talking about access permissions that would be used to restrict user access to certain endpoints, HTTP methods (GET, POST, PUT, DELETE), or limit which fields are accepted/returned in a query.
Or would access permissions more typically be stored in the application code (i.e., role-based logic compiled into the API itself)? More generically, what is the recommended way to handle custom access permissions for a RESTful API?
I'm not referring to user authentication or OAuth-based authorization on the API itself, which would be handled via standard token-based mechanisms.
We're building a RESTful API in Java EE 7 that includes a role-based access control mechanism to determine which endpoints, HTTP methods, and fields are accepted/returned in a query. In our initial design, we used custom annotations read by a Container Request Filter on endpoint methods to determine a user's permission based on their role, and we used JsonViews to filter specific entity fields during (de)serialization.
This system works but we feel it's getting overly complicated and difficult to maintain. We've discussed moving this authorization information to a few database tables, most importantly one that stores table and column access permissions (PK and FK columns excerpted).
AccessPermissions
========================
| Column | Type |
========================
| TableName | varchar |
| ColumnName | varchar |
| HasCreate | bit |
| HasRead | bit |
| HasUpdate | bit |
| HasDelete | bit |
Each role (table not shown) would reference potentially many rows in this AccessPermissions
table so that both endpoint method access and (de)serialization could be implemented by asking whether the user had an entry for the queried table and column name that allowed the requested access.
For example, a POST /endpoint1
with {"field1": "value", "field2": 23}
would require the user to be assigned a role that has any rows in AccessPermissions
where TableName = 'endpoint1'
and HasCreate = true
, and the JSON in the body of the request would deserialize only the named columns in said rows. So if the only row in AccessPermissions
corresponding to this user's role was ('endpoint1', 'field1', true, false, false, false)
then the POST
would be allowed but part of the JSON body would not be deserialized (i.e., field2
). (Also, this user would be able to POST /endpoint1
but not [GET|PUT|DELETE] /endpoint1
.)
Generally, you want to decouple as much functionality from one another as you can. Much like you wouldn't tightly couple (or reimplement) authentication or logging inside your API / app code, you want to keep authorization decoupled.
There's actually a name for that: externalized authorization. Gartner calls it externalized authorization management (EAM). There are several ways to achieve this. First of all, your development framework probably provides you with a means to define authorization on your API. Spring Security, .NET claims are such examples.
On top of that, you have authorization models:
Imagine your API is about insurance contracts and claims:
And so on... Your policies could state:
As you can see, the policy defines the high-level entitlements. These are derived from your business app's metadata (which you already have) such as the contract ID, the contract value, contract region, contract customer; claim ID, claim region; customer assigned sales rep...
There are a couple of frameworks that implement this type of approach. The one I work with the most is XACML (eXtensible Access Control Markup Language).
Some of the key benefits it brings to your API security is:
Have a look at this report on API security (disclaimer: I work for the company who wrote that piece).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With