After my post a couple of days ago about the first thing you should know about REST, a friend emailed me with this feedback:
Nice post. It was something I was thinking about just recently and I think I’m guilty of making these mistakes. The example which confused me was verifying a password. I wasn’t sure what HTTP method to use or what the resource was. The request needs to contain a password but doesn’t expect any response other than a 200, does this mean GET is inappropriate? It doesn’t update anything, unless of course it fails in which case it may update a failed login counter or lock the account. Does this rule out PUT and POST?
Here’s the response I sent him (fleshed out with a little more detail for this blog)…
REST can be easy and REST can be hard
Yep, the examples in my blog were the easy ones. Plenty of hard ones will crop up, where the resource on the server you want to manipulate is not immediately obvious, like the one you’ve pointed out, or where coming up with a good set of URL patterns is not straightforward. As with all things that aren’t easy, spending some extra time on it is usually worth the effort.
Think like a REST Server
I think what can help is to try and think less about what the client is doing (“verifying a password”) and more about what’s happening on the server side. Remember that REST is all about resources maintained by the server and manipulating those resources by exchanging representations of them. So, what’s happening on the server in this case? Most often when a password is being submitted, the resource change that’s happening on the server is that an authenticated session is created. Often, the technicalities of session management are hidden by a web framework, so it looks like the client just asked the server, “Is this password okay?” and the server said “Yep”. However, the creation of a session is the state change that’s occurring on the server. So, if the client’s purpose in verifying the password is really to create an authenticated session for the user, then perhaps the REST invocation should be:
POST /sessions While the response is a 200 (and perhaps, in view of the above suggestion, it should be a 201 “Created”?) and the body contains nothing, I suspect the whole response contains something more, in the way of a
Set-Cookie header containing a shared secret session ID which provides access to the session when passed as a
Cookie header in future requests. So the server is sending something back about the resource it created, just not in the body.
Think about what happens next
Looking at the converse of that use case – logging out – is interesting, too. The action occurring on the server is to end the session, which sounds like it would probably be communicated as:
DELETE /sessions/<session-id> To be able to make that call, though, the client needs to know the location (URL) of its session, which probably means it should have been returned in the creation response. A good way to do this seems to be to provide the URL of any newly-created resource in a
Location header in the response from the POST or PUT that created it. (Google has more information.)
Good REST patterns require good authorisation controls
It’s important to note here, though, that we’re making a decision which will effect authorisation. If the URL used for logging out were just
/logout, the server would just delete the session identified by the session ID
Cookie header. However, if we identify the session being manipulated as part of the URL, as above, then we open the possibility for people to attempt to delete other people’s sessions by fiddling with the URL. To protect against this, we’ll need authorisation controls in place to ensure that the session being deleted is the one owned by the authenticated requester. The risk we’ve introduced here by identifying resources in the URL is that it opens us up to what OWASP calls “Insecure Direct Object References“. Now it’s important to note that it’s not inherently a problem to have direct object references, only to leave them unsecured. Of course, if you don’t have direct object references (or the only ones you have should be able to be accessed by anyone, or by every authenticated requester), you don’t need to secure them with a sophisticated authorisation scheme. Hence OWASP’s two suggested controls for this risk are (1) don’t have direct references or (2) secure them with proper authorisation.
So, if you’ve committed yourself to starting to think about your REST APIs in terms of resources, my two tips for doing it well are:
- Think like a REST server: What is the resource change?
- Think about authorisation: Should everyone have access to this URL?
Want to learn more?
Image credit: ‘Casual style‘ by Gever Tulley