I'm trying to add realtime updates on a Django app which has a similar architecture as explained here: http://lincolnloop.com/blog/2012/apr/23/ginger-tech-stack/. Basically, updates are send from Django to Node.js via Redis, then pushed to connected clients with Socket.io. Right now, I can broadcast to every clients connected but in a real case, I need to send data only to clients concerned by an action (e.g.: a user post a message, only his contacts/subscribers receive this message). So I need some kind of authentication on the Node.js side to know who's who. How can this be achieved ?
-
1Assuming Django and Node are on the same hostname, and you have cookie based authentication in Django, and thus, Node can read those cookies, you could write an adapter for Django's authentication, for Node.Linus Thiel– Linus Thiel2012-05-04 11:08:42 +00:00Commented May 4, 2012 at 11:08
-
Ok, so I moved from database-backed to cookie-based session and I can read the « sessionid » cookie on Node side. So now I guess I have to port this fonction to Node...djevg– djevg2012-05-04 13:28:42 +00:00Commented May 4, 2012 at 13:28
-
Sorry -- I wasn't clear on what I meant with cookie based auth: If you can access the same database from Node, you should be able to use db-backed sessions, I just meant using cookies for session IDs. Either solution should work fine. Yeah, node's crypto module will be your friend.Linus Thiel– Linus Thiel2012-05-04 13:34:58 +00:00Commented May 4, 2012 at 13:34
-
Well, when searching for a solution I thought accessing the db from Node.js would be over-complicated but it's probably much easier than using Django signed cookies. I'm going to try this way. Thanks for pointing out ;)djevg– djevg2012-05-04 14:31:34 +00:00Commented May 4, 2012 at 14:31
-
So now I can retrieve the session data from the db based on the cookie's session id. I thought the session data was base64 but it seems to be python specific (pickle - which I'm not familiar with)… Is there a way to decode this on node side ?djevg– djevg2012-05-04 19:45:19 +00:00Commented May 4, 2012 at 19:45
|
Show 3 more comments
1 Answer
Here is my solution so far to get the identity of the connected user in socket.io/node.js (if you have better solution, feel free to post):
On Django side:
Patch Django to use json instead of pickle object to store session data:
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 5a637e2..cb4db54 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -2,9 +2,9 @@ import time from datetime import datetime, timedelta try: - import cPickle as pickle + import json except ImportError: - import pickle + import simplejson as json from django.conf import settings from django.core.exceptions import SuspiciousOperation @@ -75,21 +75,21 @@ def _hash(self, value): return salted_hmac(key_salt, value).hexdigest() def encode(self, session_dict): - "Returns the given session dictionary pickled and encoded as a string." - pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) - hash = self._hash(pickled) - return base64.encodestring(hash + ":" + pickled) + "Returns the given session dictionary as json and encoded as a string." + data = json.dumps(session_dict) + hash = self._hash(data) + return base64.encodestring(hash + ":" + data) def decode(self, session_data): encoded_data = base64.decodestring(session_data) try: # could produce ValueError if there is no ':' - hash, pickled = encoded_data.split(':', 1) - expected_hash = self._hash(pickled) + hash, data = encoded_data.split(':', 1) + expected_hash = self._hash(data) if not constant_time_compare(hash, expected_hash): raise SuspiciousOperation("Session data corrupted") else: - return pickle.loads(pickled) + return json.loads(data) except Exception: # ValueError, SuspiciousOperation, unpickling exceptions. If any of # these happen, just return an empty dictionary (an empty session).
On Node.js side:
Read the session key from the "sessionid" cookie:
socket.on('connection', function(client) { ... var cookie_string = client.handshake.headers.cookie; var parsed_cookies = connect.utils.parseCookie(cookie_string); var sessionid = parsed_cookies['sessionid']; ... });Retrieve & decode the session data from the db corresponding to the sessionid to get the user id.
2 Comments
Mark Lavin
Why patch Django when you could just write your own session backend?
djevg
Yep, probably better indeed. So I tried to extend Django db session backend: gist.github.com/9ef870e91dc034f60179 but it's not working properly. Not sure what's wrong... I can login, the cookie & db entry seem to be correctly created but when I'm accessing a login_required protected view I'm redirected to the login page...