A lot of examples about RESTful Web Services do not take into account the problem that today many applications are multi-user.
Imagine a multi-user backend exposing a RESTful API. The backend data architecture uses a shared database and shared schema. Each table will contain a reference to a tenant_id
:
+-------------+----+-----------------+
| tenant_name| id | shared_secret |
+-------------+----+-----------------+
| bob | 1 | 2737sm45sx543 |
+-------------+----+-----------------+
| alice | 2 | 2190sl39sa8da |
+-------------+----+-----------------+
+-------------+----+-------+-----------+
| pet_name | id | type | tenant_id |
+-------------+----+-------+-----------+
| fuffy | 1 | dog | 1 |
+-------------+----+-------+-----------+
| kerry | 2 | cat | 2 |
+-------------+----+-------+-----------+
Question 1: with three or more client applications (i.e Android, iOS and Web App) interacting with the RESTful backend, how would you perform authentication against the backend?
RESTful backend, API, HTTP-Verbs, shared database and schema
|
|
+---- Web Application (Client 1)
| |
| + Alice
| |
| + Bob
|
+---- Android Application (Client 2)
| |
| + Alice
| |
| + Bob
|
+---- iOS Application (Client 3)
| |
| + Alice
| |
| + Bob
|
Each client should allow Alice and Bob to manage her/his pets. Each client is a GUI, and it's going to use (internally, making HTTP requests) the backend. Question: how can each client can authenticate against the backend?
Assume HMAC (it's perfectly RESTful, no sessions): this method involves signing the payload with a shared secret (never sent over the wire). Should each client have its own copy of the tenant
table (which holds the shared_secret
field)?
Android App -> Client Sign -> Signed Request -> Backend -> Result
Web App -> Client Sign -> Signed Request -> Backend -> Result
Question 2: what should the resource URI's look like?
Here are two possibilities for ways to GET Bob's pets:
Possibility #1: The Authorization
header gives you the tenant's (unique) name:
GET /pets HTTP/1.1
Host: www.example.org
Authorization: bob:c29kYW9kYSBhb2lzYWRoIGYgZDUzNDUz
Possibility #2. The tenant_id
is sent as query parameter:
GET /pets/tenant_id=1 HTTP/1.1
Host: www.example.org
Authorization: bob:c29kYW9kYSBhb2lzYWRoIGYgZDUzNDUz
Part 1
(Thinking out loud: Have you already decided to use HTTP and HMAC? If so, why are you asking us?)
I'd suggest HTTPS with Basic Auth. Simple. It is good enough for Stripe after all.
References:
Update: Here is some additional detail on how to handle the auth:
Each client application will contact the service using an API key. Using HTTPS and Basic Auth, the client will provide its API key as the basic auth username. It does not need to provide a password, since it is using HTTPS. You need to assign an API key to each application (Web app, Android, iOS), I see two ways:
A. One option is to give each user one API key that is shared across clients.
B. Another option is to give each client a unique application.)
But how do you get the keys to the clients in the first place? Build a "key request" API endpoint. I'd suggest giving each client a "starter" key that is only used to contact this endpoint. (The starter key does not grant other access.) When a user first uses the client, he/she must authenticate. The client passes this along to the "key request" endpoint so it can generate a key associated with the user. From then on, each client has a client-tied API key.
Part 2
Consider giving each tenant a subdomain. If you use Rails (or probably any modern web stack) you can use the subdomain to lookup the tenant id. Then your API can be used like this:
GET http://tenant1.app.co/pets
GET http://tenant2.app.co/pets
GET http://tenant3.app.co/pets
References (Rails-specific but should be helpful across web stacks):
Note: as your example suggests, for simplicity, I would not re-use the same pet id for different tenants. For example, the following is a simple way to go:
GET http://tenant1.app.co/pets/200
GET http://tenant2.app.co/pets/201
GET http://tenant3.app.co/pets/202
The approach I'm describing is much cleaner than passing tenant_id
as query parameter. Besides, using tenant_id
as a parameter feels wrong. I like to use parameters for more "algorithmic" things, as I read in "RESTful Web Services" by Ruby and Richardson.
References:
By 'multi-tenant' do you simply mean application/web service users? Multi-tenant can often mean something much more complicated msdn.microsoft.com/en-us/library/aa479086.aspx.
You need to authenticate each user with your web service. This can be done with basic http authentication across SSL.
From the web services point of view you would perform authentication the same for all three clients. The service doesn't care about the type of client - that's the point. It may be that you require different representations for your clients e.g. XHTML or JSON. I like to keep things simple and always choose JSON.
For the resources, the easiest way to manage them is to have a users resource as the top level, then chain all the resources together for each user e.g.
GET users/fred/pets - returns all pets for user fred
GET users/fred/pets/sparky - returns details on freds pet sparky
The great thing about doing it this way is that you can add code to authorise each request e.g. you may have two users, fred and jack. Both users will pass authentication, but you should only allow fred to request his resources and jack to request his. You just have to add authorisation checks in your api e.g. get the username from the URI, get the username of the authenticated user, check they are the same. If not return something like http 403 forbidden, if they are the same, allow the request.
I think if you are still unclear, you need to read up on the details of REST. By far THE best book on the subject is this one RESTful Web Services. It covers REST from first principles. It also has a very nice section on designing resources, and how to manage users and multiple clients.
I'm not sure to understand the question since multi-tenancy seems a bit overkill in this case. However I can try to answer the second question.
REST is a "resource-based" architecture, and you must realize that /pets
and /pets/?tenant=1
does not refer to the same resource:
/pets
refers to the pets of the current user,/pets/?tenant=1
refers to Bob's pets.While neither solution is wrong, it is usually better to get the second solution. URIs are indeed designed to be shared, and you have more reasons to share "Bob's pets" (even if it needs authentication and authorization to be represented) than the abstract "my pets" which would be different for every user.
See Should resource ids be present in urls? for a similar discussion...
If HTTP is the protocol all 3 client-types use, you should only need to implement one authentication scheme. You can pick your poison - Basic, Digest, Oauth2, or Cookie are some oft used methods. Since it is over HTTP, I see no reason to replicate this logic. There are probably several frameworks that will abstract this for you depending on your platform.
To distinguish the client type, you can use a HTTP header. The user-agent
might already make this possible. Another option would be to use a custom header you define. Any HTTP client is capable of setting headers, and any server can handle custom header. A decent web framework will make those available to you with relative ease. As a back-end service, I suspect you will want to handle all client requests uniformly - or as much as possible. Certainly, maintaining one backend is preferable to three.
As far as what your API should look like - it is entirely up to you. To stay RESTful, how to GET a cup of coffee is well worth the read - insert obligatory link to Roy Fielding's thesis. Chances are, what you are really looking for is a guide to writing 'resourceful' links.
Of the options you list, prefer the 2nd,/pets?userId=bob
, if users might need to access all the pets in the system. Prefer the first, /pets
if a user will only ever need to access their pets.
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