Sharing Users between Django projects

by Malte Vogl

You should never share Django user databases between projects! But sometimes....

Image credits: Lorem Picsum

For a small project we had two Django instances dealing with different parts of a workflow. Since the system was supposed to run on an air-gapped sub-system of the infrastructure, our usual authentification and authorization approach using OpenIDConnect was no option.

In v0.5 we simply had two user databases, one for each instance, and had to manage the users between both systems. However, even changing the password was very complicated for the admins, not even talking about the users themselves.

In came the idea of Django’s database routers: Why not simply have system A look into the user database of system B?

The first thing that comes up on Stack Overflow is NEVER DO THIS!

Still it was a viable option for our setup. The router was relatively fast to write, but somehow it brought some strange errors?! Maybe the small toy database for one system was not capable of the complex rewrites? Ahh, that’s why sqlite3 is only for testing! Last time migrating a production database led to a full day of pain. However, this time I found a little help. The trick was to get rid of ContentType entries, and everything went smooth on first try!

The databases were now talking to each other without any error and the number of available users in system B showed that it was indeed using system A’s user database. But there was still a strange error: When going from system A to B Django always asked for an additional login process. Hmmm…

Since Django by default saved the session state in cookies, we thought maybe the cookies are not shared correctly. CSRF issues come to mind. Setting the SESSION_COOKIE_DOMAIN value for both projects led to no change. Setting SESSION_COOKIE_NAMECSRF_COOKIE_DOMAIN … Still nothing.

Digging deeper into the documentation, we realized that the state was by default not saved in a cookie, which only contains an ID, but in the database. So, time for another quick routing from system A to B, this time for the sessions part of database operations. Restarting… Nothing changed…

One more time going back to the documentation and Stack Overflow we found an obscure post, that finally brought salvation: The sessions state is indeed in the database, but signed by the secret key generated by Django when starting the project from scratch. So, accessing the session state with a wrong key raises a SuspiciousOperation error and leads to a re-signin for the user. A really basic security feature.

The final solution to our problems was thus to set the same SecretKey for both projects, and voila, everything worked as expected. A very simple and logical solution but we spend hours finding it. I’ll read 2Simple2Mention next time … ;-)