3

I have a case where I have three sites A, B, C . A is more of administrative site, B and C are consumer facing applications. Each of them have their own separate databases. What I want to do now is as follows:

I want all three A, B, C to be connected in such a way that when a new user registers at B and when registration is successful, the same user should be able to login into A or C. If there is any change in users details they reflect on A and C as well.

What I thought as solution till now is:

When a user registers at B, I can replicate user on A and C with same credentials, so this would allow user to login into any of the three sites. I was also pondering upon idea of having a central database to store django auth models, but I am not sure in that case how authentication would work.

I need suggestion as to how this can be done. Also, does this qualify to be called as SSO?

In case if someone finds question to be misleading or vague or inappropriate do mention as to why before downvoting

  • Among other ideas, you could setup an authentication service that all of your sites use to login users. And yes, it does qualify as a form of SSO. – spectras Nov 19 '15 at 07:46
  • Related: http://stackoverflow.com/questions/4662348/implementing-single-sign-on-sso-using-django – spectras Nov 19 '15 at 07:48
  • @spectras about authentication service you mentioned can I find some elaborate description about how to go forward with it? For the link on SO, I did go through that and have looked at django-mama-cas –  Nov 19 '15 at 08:38

1 Answers1

3

There are two solutions to your problem:

  1. Use routing to multiple databases. Django supports different databases for different models (more info on @ https://docs.djangoproject.com/en/1.8/topics/db/multi-db/). So you could route all queries for your auth models to the authentication database. Django documentation already provides such a configuration @ https://docs.djangoproject.com/en/1.8/topics/db/multi-db/#an-example. I've not tested this configuration however this will have the problem of not being able to use foreign keys to your user model (since it will be stored in a different database). To resolve this, you could have a UserProxy model (or something similarly named) in all your projects that will just keep the username of the User so you'll be able to create foreign key relations from your models to the UserProxy. Any user data that would need to be retrieved would be forwarded to the correct User object by the UserProxy.

  2. Use django authentication. Django can be configured to use a number of different authentication methods (check the docs @ https://docs.djangoproject.com/en/1.8/topics/auth/customizing/). For your sites B and C you can configure an authentication backend that uses the database of the administrative site (A) to login. I am using this method succesfully - here how you could do it:

  class RegUsrBackend(django.contrib.auth.backends.ModelBackend):
    def authenticate(self, username=None, password=None):

        try:
            conn = django.db.connections['users']
            cursor = conn.cursor()

            cursor.execute("select pn.password, pn.username, au.first_name, au.last_name from auth_user au where au.username = %s ", [username])
            row = cursor.fetchone()
            if row and check_password(password, row[0]):
                user, created = get_user_model().objects.get_or_create(username = row[1] )
                if user.first_name != row[2] or user.last_name != row[3] :
                    user.first_name = row[2]
                    user.last_name = row[3]
                    user.set_unusable_password()
                    user.save()
                return user
        except:
            return None

As you can see, here I've also configured a different users database (named users) and I am issuing a raw query to this database to get the user with the passed username and its password hash. After that, I check if the user exists and has the correct password (using the check_password) and if everything checks, I use get_or_create to either retrieve or create the local instance (i.e the instance of the user in the application's database) of that user. Finally, before returning the user I check to see if there's a change in his first or last name to the administrative application and update them in the local one.

Finally, you need to put this backend in the in the AUTHENTICATION_BACKENDS settings of the applications using it.

Using this method you won't have to use any UserProxy to support foreign keys (since there will exist a local User model) models but I feel that it is a more hackish method than the first one. Also, if the details of a user has been changed in the administrative database the details in the others will be updated when he logs in. Also you could change your backend to something even more exotic, for example instead of querying the database you could create a REST api in your administrative backend and use this to login to the other applications.

Finally, to answer your question about SSO, what I've described above is not SSO. SSO means that when a user logs in to a site he won't have to log in again to the others becase a single session key is kept and shared in all these sites. A common solution for SSO is to use CAS (http://jasig.github.io/cas/4.1.x/index.html) however I don't have good experience with it (and you'll need to have another, java based server to host CAS).

Serafeim
  • 14,962
  • 14
  • 91
  • 133
  • Thank you for a detailed answer, appreciate it. I am a bit inclined to your hackish approach, which I think is fair enough. I did have a look at django-mama-cas in mean time. The solution provided by you seems to be something I will implement. Thanks once again. –  Nov 19 '15 at 08:37
  • This is definitely a working solution, I am using it for more than two years for a bunch of projects and never had any problems with it! – Serafeim Nov 19 '15 at 09:05
  • @RajeshYogeshwar if Serafeim's answer was useful to you, you should remember to upvote it (the up arrow at the top of the answer). :) – spectras Nov 19 '15 at 10:05
  • I know I have to upvote it @spectras I am implementing the same and see if I can add something more to it before upvoting it. –  Nov 19 '15 at 10:21