In the Test Driven Development Book for Python and Django by Harry Percival called Obey The Testing Goat, there is a chapter about enhancing the functional test base class and adding pre-authentication so you don’t need to login via the login screen with Selenium.
It uses a custom Email Authentication Backend, but I needed to implement this on a standard: django.contrib.auth.backends.ModelBackend
.
My First Attempt
def create_pre_authenticated_session(self, user):
'''Create an authenticated user quickly'''
session = SessionStore()
session[SESSION_KEY] = user.pk
session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0]
session.save()
# visit domain (404 quickest)
self.browser.get(self.live_server_url + "/404_no_such_url/")
self.browser.add_cookie(dict(
name=settings.SESSION_COOKIE_NAME,
value=session.session_key,
path='/',
))
I ran my functional test and something weird was happening, the cookie was getting killed right after this method is called an going to any page.
So I compared based on the cookie itself compared to one that existed in firefox developer tools.
The difference was the httpOnly
thingy. So I added it…
self.browser.add_cookie(dict(
name=settings.SESSION_COOKIE_NAME,
value=session.session_key,
path='/',
secure=False,
httpOnly=True
))
Nothing changed, the cookie was still gone.
So then I compared an existing decoded session with the one created via the method above.
To find the decoded session:
$ python manage.py shell [...] In [1]: from django.contrib.sessions.models import Session # substitute your session id from your browser cookie here In [2]: session = Session.objects.get( session_key="8u0pygdy9blo696g3n4o078ygt6l8y0y" ) In [3]: print(session.get_decoded()) {'_auth_user_id': 'obeythetestinggoat@gmail.com', '_auth_user_backend': 'accounts.authentication.PasswordlessAuthenticationBackend'}
The Session Difference
I noticed there was a difference a working session looked like this:
{'_auth_user_id': '1', '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend', '_auth_user_hash': '6a34097f6dab2a1fc68f262e9e67186d2ad5ba93'}
whereas the one I created looked like this:
{'_auth_user_id': 1, '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend'}
So the _auth_user_hash
was a problem. I search the django source and found it in auth.
So I set the hash session key with: session[HASH_SESSION_KEY] = user.get_session_auth_hash()
It then worked.
The Solution
def create_pre_authenticated_session(self, user):
'''Create an authenticated user quickly'''
session = SessionStore()
session[SESSION_KEY] = user.pk
session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0]
session[HASH_SESSION_KEY] = user.get_session_auth_hash()
session.save()
# visit domain (404 quickest)
self.browser.get(self.live_server_url + "/404_no_such_url/")
self.browser.add_cookie(dict(
name=settings.SESSION_COOKIE_NAME,
value=session.session_key,
path='/',
secure=False,
httpOnly=True
))