Skip to content

Instantly share code, notes, and snippets.

@tianchu
Last active June 1, 2023 21:44
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save tianchu/ee2e825ccccea407edb1 to your computer and use it in GitHub Desktop.
Save tianchu/ee2e825ccccea407edb1 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
};
@sandiprb
Copy link

While this is if definately a great idea, do you think there might be certain security risks while playing with sessions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment