Skip to content

Instantly share code, notes, and snippets.

@riegie
Forked from tianchu/django_auth.js
Created September 8, 2016 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save riegie/9c65b1211e2c773838c05db73a6c199c to your computer and use it in GitHub Desktop.
Save riegie/9c65b1211e2c773838c05db73a6c199c to your computer and use it in GitHub Desktop.
How to authenticate Django users in node.js
/*
* Authenticate Django users in node.js.
*
* Django is great for many projects, while node.js does some fantastic
* jobs that Django couldn't. For example, you may have a Django app
* managing your user accounts and another real-time service or application
* running on Node, then you probably will need to read Django user session
* to authenticate users in the Node project.
*
* This gist is not production ready yet, but it demonstrates how could it
* be done and where to start.
*
* Note, your Django project has to be using redis as session backend, and
* serializing user session objects in json.
*
* Tested on:
* - Django 1.7.1
* - Node.js 0.10.35
*/
var django_session = require('./django_session.js');
// TODO: Randomize it
var session_id = "z8dfbzthqwjru1rchan89owg5p3x8v39";
function anonymous(callback) {
django_session.setSession(session_id, {}, function(err){
callback(err, session_id);
});
}
function login(user_id, callback) {
django_session.setSession(
session_id,
{
// TODO: Create a real hash
"_auth_user_hash": "077ff3aaedd42e66642e2c7df037d1ced2ed6fbf",
"_auth_user_backend": "django.contrib.auth.backends.ModelBackend",
"_auth_user_id": user_id
},
function(err){
callback(err, session_id);
}
);
}
function logout(session_id) {
django_session.setSession(session_id, {}, function(err){
callback(err, session_id);
});
}
module.exports = {
anonymous: anonymous,
login: login,
logout: logout
};
/**
* Read and parse Django user session, so we can perform authentication &
* authorization actions in node.js project.
*
* Currently we assume Django user session is stored in a redis server, but
* this lib should be able to be extended for other session storage backend.
*
* A raw django session is encoded by utf-8 & base64. After decoding a typical
* session looks like (no line breaks):
*
* 8dd6ffb4e993a6a3bbe3d986452f2746db737132:
* {"_auth_user_hash":"077ff3aaedd42e66642e2c7df037d1ced2ed6fbf",
* "_auth_user_backend":"django.contrib.auth.backends.ModelBackend",
*"_auth_user_id":1, "test":123}
*
* Keys prefixed with "_auth" only exist for authenticated users.
*
*
* Usage: getSession(session_id, callback) gets session data from django
* session store, wrap it in a session object, which is passed into
* callback function. The session object contains not only the original
* value of the session data, but also a set of helper attributes.
*
* E.g.,
* getSession("t98h2r94r3934r53", function(err, session){
* console.log(session);
* console.log(session.user_id);
* });
*
*
* Note, we haven't implemented django's session hashing function here.
* So updateSession and setSession are not yet ready for use other than
* testing until we can clone django's hashing algorithm here.
* Refer django/contrib/sessions/backends/base.py for details.
*/
var redis = require("redis"),
_ = require("underscore"),
config = require("../../config/config.js");
// Build connection
var client = redis.createClient(
config.redis.django_session.port,
config.redis.django_session.host,
{}
);
client.on("error", function (err) {
console.log("Error connecting redis: " + err);
});
function buildSessionKey(session_id) {
/* Build redis key for a session id. */
return "session:" + session_id;
}
function getSession(session_id, callback) {
/*
* session_id: A string contains django session id.
* callback: After session retrieved, callback will be called with
* a session object contains following attributes:
* - value: A JSON object contains parsed session value.
* - raw_value: A raw B64 encoded string containing session.
* - user_id: A short-cut to user id.
*/
// TODO: Validate the hash, make sure session is not corrupted.
client.get(buildSessionKey(session_id), function(err, reply) {
if (err) {
callback(err, null);
} else {
var raw_value = new Buffer(reply, 'base64').toString('utf-8');
var session_value = JSON.parse(
raw_value.match(/^[a-z0-9]+:({.*})$/)[1]
);
var session = {
value: session_value,
raw_value: raw_value,
user_id: session_value._auth_user_id || null
};
callback(err, session);
}
});
}
function updateSession(session_id, session_value, callback) {
/*
* If session_id already exists, update its value by merging session_value
* into it.
*
* If session_id doesn't exist yet, which is NOT supposed to happen in a
* normal working environment, we should return an error.
*
* session_id: A string contains django session id.
* session_value: A JSON object contains the value to be added to session.
* callback: After session set, callback will be called with err and reply.
*/
getSession(session_id, function(err, session) {
if (err) {
callback(err, null);
} else {
session_value = _.extend(session.value, session_value);
setSession(session_id, session_value, callback);
}
});
}
function setSession(session_id, session_value, callback) {
/*
* Set session value for a specific session_id.
*
* session_id: A string contains django session id.
* session_value: A JSON object contains the value to be set as session.
* callback: After session set, callback will be called with err and reply.
*/
// TODO: Use a real hash, so django won't treat session as corrupted.
var hash = new Buffer("An invalid node.js session hash").toString('hex');
var encoded_session = new Buffer(hash + ":" + JSON.stringify(session_value)).toString('base64');
client.set(buildSessionKey(session_id), encoded_session, function(err, reply) {
callback(err, reply);
});
}
module.exports = {
getSession: getSession,
updateSession: updateSession,
setSession: setSession
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment