I have a Restful web service API, that is being used by different 3rd parties. Part of that API is restricted (you need username/password to access it). I was wondering what would be the best way of implementing authentication?
I'm using https, so communication is encrypted. I have two ideas:
- Before user starts using (restricted) service, it sends username/password using POST (since https is being used credentials are encrypted). After the login is successful, server sends back random single-use value (nonce) that is matched with this username. When next request is being made, along side a username, client sends previously returned nonce. Servers matches username and nonce and returns new nonce along side requested data. Each new request uses new nonce. Basically, this is a lightweight version of Digest access authentication.
- Since this API is used from 3rd party, username/password could be used for each (restricted) request. Since https is being used, they will be encrypted. Downfall of this approach is the fact that this wouldn't be Restful compliant (POST would be used always).
I'm much closer of choosing first approach (it is Restful compliant, relatively easy to implement, XML, json or html can be used without changing anything), but I wanted to see what is your opinion? What do you recommend: first, second or some third approach?
Btw, I'm using Python at server side.
One way I've seen this done in APIs (and the way am currently implementing it) is to create a RESTful resource called Session which is created via a POST which supplies a username and password.
Here is basically how I've implemented it:
POST /sessions { Username: "User", Password: "Password" }
Create an time limited session and returns the session resource which contains the session key value and expiry. You may also want to return this as a cookie value for the convenience of implementation of API clients.
DELETE /session/{id}
Immediately expires the session so it can no longer be used. This is used for explicit sign-outs.
I then have the user attach the session key via a query parameter, though you can also allow it to be submitted via a cookie value, I'd recommend allowing for both.
What I prefer about this is that it is extremely simple.
Obviously your scenario will dictate somewhat how your sessions should be managed, perhaps they are not time limited and last indefinitely, and perhaps they are hashed or encrypted for added security.
If you are using HTTPS everywhere you probably don't need to worry too much. However, if you want to use HTTP, you will need to use something like a hash along with a secret key and say a time stamp to generate a secure key per request. This way you can share the secret key over HTTPS and then switch to HTTP for further calls. Even if someone manages to sniff out the key from a request it can expire almost immediately and be useless.
Disclaimer: I am not a security expert ;-).
There's no reason to not just use HTTP authentication here.
That said, the concept of POSTing to get a time block nonce can work well. But it's the motivations as to why you would need to jump through that extra hoop in the first place.
This technique was considered when using a bcrypt hash for the original password, because of the actual expense of validating a user (if you don't know, bcrypt can be tuned to take significant real time to perform the hash function). The choice was made to provide the option to have the service "log in" once using the password that would go through the expensive validation process via bcrypt, and would then get a time blocked token in return for future requests that would bypass the bcrypt process.
In the case of the bcrypt process, use HTTP Authentication, the service would work with both the normal password as well as with the token. That way the user could always use the password for their service, but it just becomes expensive. So they CAN do this, they just SHOULDN'T. The service doesn't care which authentication technique the client uses.
The nonce service is offered as an aside to improve throughput.
Other than that, it's standard HTTP authentication, but with a new scheme.
Amazon web services does it well, check out there methodology for some ideas. Essentially they get clients to encrypt a special http header using their password.
Here's the link:
http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
Assuming the service is never consumed in a browser and the communication is encrypted anyways, i see no harm in a variation of the second method: Add X-Headers to send username/password with each request, e.g.:
GET /foo HTTP/1.1
Host: www.bar.com
X-MyUsername: foo
X-MyPassword: bar
Another idea would be to use HTTP Basic Auth and just send a Authorization: Basic base64(user:password)
-Header. That is, if the connection is always encrypted.