Reputation: 133
I am quite new to Python and CherryPy and trying to build a basic web application which will query data from the server with a RESTful API. I am trying to do it the right way from the start. One part I have not been able to figure out is authentication for the API as REST is supposed to be stateless, and you don't use sessions.
I want to be able to use my API with "native clients" which does not have Cookies, so using session Cookies is not an option. The data will be accessed with AJAX in an HTML. OAuth seems like an option but I dont want to rely on a third party to provide the login service (Facebook was offline for almost a day a few weeks ago)
Can anyone point me in the right direction, which would work with CherryPy?
Upvotes: 1
Views: 2916
Reputation: 25234
There is no "the right way" for RESTful authentication. REST is not an API silver bullet per se. There are you requirements to it, and solutions with their pros and cons that you need to weigh. However, I'll tell about HTTP standard methods that will work on CherryPy out-of-the-box.
The article you linked in your comment is pretty clear about the simple way to do authentication in stateless fashion -- Basic Auth on HTTPS. There's also Digest Auth, which the doesn't transfer password as such and prevents replay attacks, so it's fine to use it on plain HTTP.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cherrypy
userpassdict = {'user1': 'passwd'}
checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(userpassdict)
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
},
'/' : {
# HTTP verb dispatcher
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
# JSON response
'tools.json_out.on' : True,
# Basic Auth
'tools.auth_basic.on' : True,
'tools.auth_basic.realm' : 'Walled garden',
'tools.auth_basic.checkpassword' : checkpassword,
# Digest Auth
#'tools.auth_digest.on' : True,
#'tools.auth_digest.realm' : 'Walled garden',
#'tools.auth_digest.get_ha1' : get_ha1,
#'tools.auth_digest.key' : 'put random secret here',
}
}
class Document:
'''Test like:
curl --user user1:passwd --request GET http://localhost:8080/api/document
curl --user user1:passwd --request GET http://localhost:8080/api/document/2
curl --user user1:passwd --request POST --data name="new entry" http://localhost:8080/api/document
curl --user user1:passwd --request PUT --data name="new entry2" http://localhost:8080/api/document/4
curl --user user1:passwd --request DELETE http://localhost:8080/api/document/4
'''
_store = None
exposed = True
def __init__(self):
self._store = {
1 : {'id': 1, 'name': 'foo'},
2 : {'id': 2, 'name': 'bar'},
3 : {'id': 3, 'name': 'baz'},
4 : {'id': 4, 'name': 'qux'},
}
def GET(self, id = None):
if id:
return self._store[int(id)]
else:
return self._store.values()
def POST(self, **kwargs):
id = max(self._store.keys()) + 1
self._store[id] = {'id': id, 'name': kwargs['name']}
return id
def PUT(self, id, **kwargs):
self._store[int(id)].update(kwargs)
def DELETE(self, id):
self._store.pop(int(id))
if __name__ == '__main__':
cherrypy.quickstart(Document(), '/api/document', config)
Upvotes: 3