Can't read session data from database

I have inherited a legacy Django application (currently on 2.2.24), that I am trying to modernize step by step. One requirement is to move from MySQL to an Azure SQL database.

Unfortunately, I ran into an issue, where I just can’t read back the session data from the database!

This is what my settings.py looks like. As you can see, django.contrib.sessions is set, as well as django.contrib.sessions.middleware.SessionMiddleware. Also, I have explicitly set SESSION_ENGINE = 'django.contrib.sessions.backends.db', which should be the default.

# ...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_swagger',
	# ...
]
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'simple_history.middleware.HistoryRequestMiddleware',
]
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
]
DATABASES = {
    'default': {
        'ENGINE': 'mssql', # https://github.com/microsoft/mssql-django
        'NAME': '<hiden>',
        'USER': '<hiden>',
        'PASSWORD': '<hiden>',
        'HOST': '<hiden>',
        'PORT': '1433,
        'OPTIONS': {
            'driver': 'ODBC Driver 17 for SQL Server',
        }
    }
}
# ...

This is what the misbehaving views.py looks like, which is part of an Azure AD login procedure (def auth(request) => sso/login & def complete(request) => sso/complete)

After execution leaves def auth(request), I can see a new entry in the table dbo.django_session. However, when execution enters def complete(request), the session dictionary is empty. Also request.session.session_key doesn’t hold anything. However, I see a session cookie is getting set!!!

@never_cache
def auth(request):
    request.session.flush()
    
    nonce = str(uuid.uuid4())
    request.session['nonce'] = nonce # store to session db

    state = str(uuid.uuid4())
    request.session['state'] = state # store to session db

    backend = AzureActiveDirectoryBackend()
    redirect_uri = AZURE_AD_REDIRECT_URI or request.build_absolute_uri(reverse(complete))
    login_url = backend.login_url(
        nonce=nonce,
        state=state,
        redirect_uri=redirect_uri
    )

    if settings.DEBUG:
        print('login_url:', login_url)

    return HttpResponseRedirect(login_url)

@never_cache
@csrf_exempt
def complete(request):
    backend = AzureActiveDirectoryBackend()
    method = 'GET' if backend.RESPONSE_MODE == 'fragment' else 'POST' # form_post / fragment
	
    # EMPTY !!!
    s_key = request.session.session_key
    keys = request.session.keys()
    items = request.session.items()

    # EMPTY AS WELL 
    original_state = request.session.get('state') # dictionary from SessionMiddleware

    resp_meta = getattr(request, method) # request.GET / request.POST
    state = resp_meta.get('state')

    if original_state != state:
        return HttpResponse('<h1>State is lost</h1>\n\nRefresh the page (F5).')

    if "error" in resp_meta:
        return HttpResponse('<h1>%s</h1> \n %s \n\n error_uri:<a>%s</a>' % (
            resp_meta['error'], resp_meta['error_description'], resp_meta['error_uri']))

	# ...

Other things I have tried without success:

  • Starting with a fresh database
  • Recreating the SECRET_KEY
  • Calling request.session.modified = True
  • Setting SESSION_CCOKIE_SECURE = False

What is going on here? Any help on this is greatly appreciated!

Can you check the database directly to verify that the table has been created? You can then determine whether the issue is that the data isn’t being written (empty table) or that something is going wrong with reading the data (table is populated, but nothing is coming back).

You should also be able to see the sessionid cookie from the browser’s developer tools in the request. You can then ensure that the sessionid is the same between the requests, and that both match the corresponding key in the table.

I assume the login url redirect sends the user to an external login page. Right before that the session is deleted with the flush call. The redirect should set the new session cookie, but there might be an issue with the SameSite setting (or lack thereof in Django 2.2) during the redirect or when the user is sent back to your app. That would explain why you see the cookie but it isn’t actually available.

Thanks everyone for replying. Setting CSRF_COOKIE_SAMESITE = None and SESSION_COOKIE_SAMESITE = None made it work.

As I was updating from 2.0.x to the latest 2.x version this broke my setup.

https://docs.djangoproject.com/en/3.0/releases/2.1/#samesite-cookies