Sometimes I need to write my way through a concept to an implementation outline.
So I am working on a REST[R] API, and trying to think through the ways I need to manage access. The API needs to be accessible via html forms as well as custom clients. Beyond that, it is a simple CRUD[en.WT] at this point, so access control is necessary.
The output needs to be available at least as html, xml, and json. Implementing these is relatively straightforward, what is not obvious to me is best practice for determining which to use.
Xml.com[R] suggests there are four generic models:
- Server-driven negotiation
- Client-driven negotiation
- Proxy-driven negotiation
- URI-specified representation
These seem to boil down to two actual models – server-driven (reading HTTP headers Accept, Accept-Charset, Accept-Encoding, Accept-Language, and User-Agent), and URI-specified (endpoints, query values, or POST/PUT values.) That is, eventually the client/proxy side has to tell the server what representation they want to get, or the server has to make certain assumptions based on what it knows about the client/proxy.
So, addressing the first problem first.
There are two elements to the access control issue: which clients/proxies may consume the API, and which users of those clients/proxies may alter the database. Both should be addressed by tokens. Although it would be more secure for each token to be one-use tokens, most likely the client token will need to be multi-use. On the other hand, user identity is more-sensitive as the individual can accrue database altering rights; each DB altering connection must involve a single-use renewing token so it may be simplest to have all transactions involving a user be tokens.
A client token needs to identify the application/website/script which is making the request uniquely. It should also include the time at which the token was initially issued, giving the option of disallowing access by age.
A user token is actually a log-in token: it requires a username/id and password exchange over SSL, which returns a hashed token. The hash includes the user’s id, the client token, and a timestamp as well as a nonce. It should be a rolling timestamp; every transaction receives a new timestamp, in theory allowing a user to be logged in continuously. If a user attempts a transaction after their token has expired they are forwarded to a login page (if the representation is html; else they receive an error indicating not-logged-in) to receive a new user token.
Now then, back to the problem of finding out what representation is desired… I think I need to know if an html form can set various accept headers. If so, the cleanest route would then be to respond to accept headers, with or without a ‘q’ parameter[R]. If not, then it would be better to rely on a passed variable or query value, e.g. domain.tld/noun/12345.xml versus domain.tld/noun/12345.json versus domain.tld/noun/12345.html (which is also the default for domain.tld/noun/12345). Although that last looks pretty darned elegant/simple.