Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: how to store subdomain-based authentication usernames?

I need to create a subdomain based authentication system, like the one 37signals, freshbooks, codebase use. That is, each subdomain of my main application needs to have its own username namespace. I would like to keep as much as possible of the django authentication system.

What is a good way to store the username?

In particular, it should be possible for different users to have the same username as long as their account belongs to a different subdomain.

Some approaches I've considered, for which I can foresee shortcomings:

  • storing some prefix in the username field of the django auth user model.
  • extending the user model according to this.
  • customizing the source of auth to my needs
like image 377
rz. Avatar asked Nov 03 '09 02:11

rz.


2 Answers

I have built this functionality for several sites in the past and have found that your first bullet point is the way to go.

It means that you don't have to make massive change to django auth. What I did was set up a custom authentication backend that abstracts away the way usernames are stored.

auth_backends.py

from django.contrib.auth.backends import ModelBackend

from Home.models import Account

class CustomUserModelBackend(ModelBackend):
    def authenticate(self, subdomain, email, password):
        try:
            user = Account.objects.get(username=u'%s.%s' % (subdomain, email))
            if user.check_password(password):
                return user
        except Account.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return Account.objects.get(pk=user_id)
        except Account.DoesNotExist:
            return None

For this particular project Account was the user model and it just inherited directly from User however you could replace Account with whatever you want.

You have to install the custom auth backend in your settings file:

AUTHENTICATION_BACKENDS = (
    'auth_backends.CustomUserModelBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Then when you call authenticate you need to pass in the subdomain, email and password.

You can also add some other helper functions or model methods that help with making sure that only the user's actual username is displayed, but that is all pretty trivial.

like image 119
Aaron Vernon Avatar answered Oct 01 '22 08:10

Aaron Vernon


I think this may be a good use case for using django.contrib.sites in combination with the second bullet item you mentioned. You could create a CustomUser model like so:

from django.contrib.sites.models import Site

class CustomUser(User):
    """User with app settings."""
    sites = models.ManyToManyField(Site)

Then you could write a custom auth backend to check that the user can sign in to the current subdomain using the supplied credentials. This allows you to have one username for multiple sites (subdomains) without having to hack the internal auth app or store multiple usernames with custom prefixes.

EDIT: you can get the current site by using Site.objects.get_current() and then check to see if the current site is in the user's sites.

You can read more about the sites framework here: http://docs.djangoproject.com/en/dev/ref/contrib/sites/

like image 45
richleland Avatar answered Oct 03 '22 08:10

richleland