15

Two answers to this question, depending on whether sharing is across different sites or different subdomains Second answer: Multiple Django apps, shared authentication

A user goes to site1.com and logs in. Now, if he goes to site2.com, then he should already be logged in (authenticated) at that site.

site1.com and site2.com are handled by different Django apps on the same sever.

I get that the sites can share the database containing the authentication tables. What I don't get is how the session data is handled. After logging in to site1, the user goes to site2. Here he always has request.user = "AnonymousUser: AnonymousUser" instead of a user_id.

I have set it up as here: https://docs.djangoproject.com/en/dev/topics/db/multi-db/ :

site1's settings has a single database that contains the auth models as well as some other data tables. site2's settings has 2 databases. One with its own data tables and also the one used by user1. I essentially copied class AuthRouter and set up the database router.

Is it not possible what I am trying to do? I don't actually understand how the two sites can share session data. Do I need something special outside of Django? Or should this work? I can include my code here, but don't want to confuse the issue if my basic thinking about this is wrong.

EDIT: here is my setup. I am trying this on localhost.

Site1 is running on localhost:8080

site2 is running on localhost:8000

SITE2 APP:

db_router.py:

class AuthRouter(object):
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'auth':
            return 'the_ui'
        return None
    # same for write

    def allow_syncdb(self, db, model):
        if db == 'the_ui':
            return model._meta.app_label == 'auth'
        elif model._meta.app_label == 'auth':
            return False
        return None

class OtherRouter(object):
    def db_for_read(self, model, **hints):
        return "default"
    # same for write, relation, syncdb

settings.py:

DATABASE_ROUTERS = ['site2_app.db_router.AuthRouter', 'site2_app.db_router.OtherRouter']
SESSION_COOKIE_DOMAIN = 'http://localhost:8080'
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

DATABASES = {
    'default': {
        # ...
    },
    'the_ui': {
        # ...
    }
}

SITE 1 APP:

# no router
# only single database, same as the "the_ui" used in site2
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
Community
  • 1
  • 1
user984003
  • 28,050
  • 64
  • 189
  • 285

4 Answers4

21

The marked answer is correct based on the initial question of using different sites.

Here is the answer for different subdomains, eg www.site.com and shop.site.com

Use the shared database authentication as described in the question. And then, in both settings.py:

SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
SESSION_COOKIE_DOMAIN = '.site.com' #notice the period
SESSION_COOKIE_NAME = 'my_cookie'
SECRET_KEY = "" the same in both settings.py

There might be some issue about what happens if you have other subdomains that should NOT share this information. Or, maybe not, if you give their cookies different names??

Not sure if this can work on localhost.

user984003
  • 28,050
  • 64
  • 189
  • 285
  • 3
    It doesn't work on `localhost` but it works for `site.dev` if you add `127.0.0.1 site.dev` to `/etc/hosts` – Sergey Feb 27 '17 at 22:11
  • @Sergey, this is a super helpful tip. I added `127.0.0.1 dev.example.com` to /etc/hosts (where example.com is my site), so now I can log in without uncommenting `SESSION_COOKIE_DOMAIN = '.example.com'` and I don't get the error `The host site.dev:8000 does not belong to the domain awesound.com, unable to identify the subdomain for this request` (I use django-subdomains, see http://django-subdomains.readthedocs.org/en/latest/). Thank you! – Mark Mar 16 '17 at 00:39
  • As for 2017 (django 1.10-1.11) is this still a valid reply? Can't pass thru the login screen in Django Admin when using `'.mydomain.com'`, seems that Django isn' t setting the cookie – Luis Alberto Santana Aug 18 '17 at 15:58
3

As you said, the two sites can have the same authentication data by sharing the database or syncing the Users table between their respective databases.

This will ensure any user of site1.com will automatically become a member of site2.com and vice versa.

But your requirement of- any user who logs into site1.com should get automatically logged in site2.com is a bit tricky. What you really need is Single Sign On (SSO).

Why it can't be achieved by merely sharing the database (including session data) is because site2.com can never gain access to a cookie set by site1.com on the browser because of cross domain issues.

There are many SSO solutions using Django. Have a look at this SO question. Though I have never used it, Django-openid seems a good option.

Community
  • 1
  • 1
Sudipta
  • 4,773
  • 2
  • 27
  • 42
0

You can use database routers for specifying which database should be used for auth backend.

Here I have given a example router code below:

class UserSessionRouter(object):

    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'auth':
            return 'usersandsessions'
        elif model._meta.app_label == 'accounts':
            return 'usersandsessions'
        elif model._meta.app_label == 'sessions':
            return 'usersandsessions'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'auth':
            return 'usersandsessions'
        elif model._meta.app_label == 'accounts':
            return 'usersandsessions'
        elif model._meta.app_label == 'sessions':
            return 'usersandsessions'
        return None

Then specify router using the database setting DATABASE_ROUTERS and SESSION_COOKIE_DOMAIN as given below

DATABASE_ROUTERS = ['site2.routers.UserSessionRouter']
SESSION_COOKIE_DOMAIN = 'site1.com'
arulmr
  • 8,620
  • 9
  • 54
  • 69
  • I tried including SESSION_COOKIE_DOMAIN, which I hadn't before. It's still not working. I have now included my setup code. – user984003 Aug 14 '13 at 13:44
0

As Sudipta mentioned, openid is one way to accomplish SSO.

Another way is to use SAML directly (there are some tools out there for this), or a hosted service like Stormpath (https://stormpath.com) which does SSO stuff for you, and provides directly support with Django's auth system: https://github.com/stormpath/stormpath-django

I work at Stormpath, so pretty biased, but figured I'd chime in as there's quite a lot of confusion around regarding SSO + Django solutions.

rdegges
  • 32,786
  • 20
  • 85
  • 109