tags:

views:

148

answers:

3

I'm wanting to make an API quickly, following REST principles - for a simple web application I've built. The first place the API will be used is to interface with an iPhone app. The API only needs handle a few basic calls, but all require authentication, nothing is public data.

  • login/authenticate user
  • get list of records in users group
  • get list again, only those that have changed (newly added or updated)
  • update record

So, following REST principles, would I setup the uri scheme?:

  • mysite.com/api/auth (POST?)
  • mysite.com/api/users (GET)
  • mysite.com/api/update (POST?)

and the responses will be in XML to begin with, JSON too later.

  1. On the website, users login with email and password. Should I let them get a 'token' on their profile page to pass with every api request? (would make the stand alone '/auth' URI resource redundant).

  2. Best practices for structuring the response xml? It seems like with REST, that you should return either 200 ok and the XML or actual proper status codes i.e. 401 etc

Any general pointers appreciated.

A: 

You're generally on the right track. The URI scheme should be based around the idea of resources and you should use an appropriate method to do the work.

So, GET to retrieve info. POST (Or maybe PUT) to create/change resources. DELETE for well, delete. And HEAD to check the metadata.

The structure of your XML doesn't have much to do with a RESTful API, assuming it doesn't require state management. That said, you have the right idea. If it's a good request, return the desired XML (for a GET request) and status code 200. If it's a bad request, you may also, and in some cases needed to, return something other than just the status code. Basically, get familiar with the HTTP spec and follow it as closely as possible.

George Marian
+1  A: 

1- for auth, you might want to consider something like http-basic, or digest auth (note - basic in particular is insecure if not over https)

for the urls scheme:

  • /api/auth is not needed if you leverage basic or digest.
  • /api/group/groupname/ is probably more canonical
  • /api/update would generally be done as /api/users/username (POST) with the new data added - the resource is the user - POST is the verb
  • otherwise, basically your API looks sane, much depends on whether groups are hierarchical, and users must live in a group - if so, your urls should reflect that and be navigable.

2- status codes should reflect status - 200 for OK, 401 for access denied, 404 for not found, 500 for error processing. Generally you should only return an XML record if you have a good request

jayshao
Right - doing traditional web-app-like auth creates state, which is un-restful. OP will want to leverage HTTP auth, or some other scheme, which doesn't require the client to hold on to cookies, etc.
timdev
500 should not be used for most client side errors. I.e. if the error is detectable, there should be some way to inform that client about it.
George Marian
@georgemarian it's perfectly reasonable to return an XML error object, or some meaninful description w/status code 500. It is however a useful indicator that there was some error (e.g. malformed request) as opposed to "didn't return any records"
jayshao
Thanks, I still have more to understand regarding the url naming and navigating.To have api/users/username, are you meaning the actual user's username / id value would be there? which I guess could be handled with rewrite. Same Q for groups/groupname?Also, would it be good to have the response format desired as part of the resource name instead of a param? i.e. mysite.com/api/users.xml or .json (and i could handle with a rewrite)
gio
@jayshao Correct, but 500 should be used for server side errors. Specifically, `Internal Server Error`, as it is defined. `400 Bad Request` should be used for the example you give.
George Marian
@georgemarian you're right, invalid request was a poor example.
jayshao
@gio yes - to be RESTFul, your API should be written in terms of resources. So if a resource is say a user, then that user should be represented by a URL, something like GET /api/user/jayshao for instance could return an XML user record. A group, might call GET /api/groups/moderators and get back a list of URLs to user records. To add a user, you could PUT /api/user/johnqsmith the same XML format document to add a new record.
jayshao
@jayshao My original comment was intended to state the HTTP spec should be followed as closely as possible. Specifically, 500 should indicate an unrecoverable, server side error.
George Marian
Thanks again. if the client here is an iPhone app. Could I use http auth, provide user+pass, get a token (store token in MySQL with a expiry date) and then pass that with every subsequent request?
gio
@gio if you did auth, you would use credentials on every request - those *could* be a token, or they could be the username currently stored in MySQL. It's become typical to separate credentials used by the user from APIs, since it makes managing devices, and rescinding access simpler. But - you don't need to go through a login service at that point - just send the credentials with every request.
jayshao
Right, perhaps what I'll do then is just generate and display an api token on the user's private profile page. Then I can remove the login resource and they can pass that token (in the header) with every request.
gio
A: 

Authentication in an API always works by sending some authenticating token in the request header. I.e., even when using the separate /auth login approach, you would return some token, possibly a cookie, back to the user, which would need to be send together with every request.

HTTP already provides a dedicated header for this purpose though: Authorization.
The most basic HTTP "Authorization" is HTTP Basic access authentication:

Authorization : Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

Digest Authentication is another, more secure, scheme. You can use this header field for any form of authentication you want though, even your custom implemented authentication.

Authorization : MyCustomAuthentication foo:bar:n293f82jn398n9r

You could send the aforementioned login token in this field. Or you could employ a request signing scheme, in which certain request fields are hashed together with the password of the user, basically sending the password without sending the password (similar to digest authentication, but you can use something better than md5). That obliterates the separate login step. AWS employs this method.

For an API in general, make good use of the HTTP status codes to indicate what is happening.

deceze
gio
the auth schemes go in as http headers
jayshao
@gio Try to follow the linked AWS documentation. The basic idea is that you hash the content of your request together with a secret key. The resulting blob is unique for each request and reveals nothing about the password/key. The server will do the same thing, hash the contents of your request together with your secret key (which it knows) and compare the result to what you have sent. Only if you have the correct secret key can you create the correct signature for the request. It's simple, but effective.
deceze