What is the correct way to authorize and structure a RESTful backend

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 (ie 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:

  • Securing an API: SSL & HTTP Basic Authentication vs Signature
  • For a nice comparison of HMAC against alternatives, see What is HMAC Authentication and why is it useful?
  • One caveat pointed out by AviD on "Is BASIC-Auth secure if done over HTTPS?" "SSL only protects until the webserver - any internal routing, server logging, etc, will see the plaintext password."
  • 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):

  • Writing Multi-Tenant Applications in Rails
  • Adding multi-tenancy to your Rails app: acts_as_tenant
  • Multitenancy in Rails
  • 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:

  • Multi-Tenant Database Architecture – Part 5
  • "Query string parameters are appropriate if they are inputs to a Resource which is an algorithm Otherwise, these values should be moved into the URI" - Resource Oriented Architecture on Wikipedia

  • 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 eg 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 eg

    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 eg 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 eg 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...

    链接地址: http://www.djcxy.com/p/21972.html

    上一篇: 如何版本REST URI

    下一篇: 授权和构建RESTful后端的正确方法是什么?