[Django] #25392: Auth without sessions

15 views
Skip to first unread message

Django

unread,
Sep 12, 2015, 9:02:34 AM9/12/15
to django-...@googlegroups.com
#25392: Auth without sessions
-------------------------------+--------------------
Reporter: rdaysky | Owner: nobody
Type: Uncategorized | Status: new
Component: contrib.auth | Version: 1.8
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------+--------------------
Currently, the authentication middleware requires the session middleware,
while in many cases, particularly when building an API-only backend,
sessions are completely unnecessary, and what’s worse, they cause a write
query during handling of otherwise read-only requests.

Instead, the authentication system should permit operation without
sessions altogether:
1. If sessions are unavailable, request.user should stay AnonymousUser
until user calls auth.login.
2. If sessions are unavailable, auth.login should be fine not saving
anything to sessions.

Additionally, in cases both a traditional Web interface and an API (where
authentication is handled by keys in HTTP headers or request parameters)
are present, it would be useful to be able to turn the session mechanism
on an off (or rather, to instruct the authentication mechanism not to
touch sessions) on per-request basis.

Additionally, when sessions are, in fact, made use of, they should not
perform write queries until something is actually saved into a session,
for performance reasons.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Sep 12, 2015, 6:54:03 PM9/12/15
to django-...@googlegroups.com
#25392: Auth without sessions
-------------------------------+--------------------------------------

Reporter: rdaysky | Owner: nobody
Type: Uncategorized | Status: new
Component: contrib.auth | Version: 1.8
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------+--------------------------------------
Changes (by timgraham):

* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0


Comment:

Thanks for the proposal. Are you interested in providing a patch for this?
Without look at the details, I'm wondering if this might be handled better
as a separate app since `contrib.auth` seems somewhat tightly coupled to
sessions in my mind. A separate app might work better than trying to have
one application cater to both use cases (although again, it's hard for me
to say how much code duplication would be required). Are there any third-
party applications that provide this functionality? It would probably be a
good idea to raise the idea on the DevelopersMailingList to get feedback
on how to proceed.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:1>

Django

unread,
Sep 12, 2015, 6:54:35 PM9/12/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------
Changes (by timgraham):

* version: 1.8 => master
* type: Uncategorized => New feature


--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:2>

Django

unread,
Sep 12, 2015, 7:09:02 PM9/12/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by rdaysky):

Doing this would not be complex because the middleware in question isn’t
complex. In my project, I work around the issues with just the code below.

And maybe the problem of the session DB backend making a write query upon
first attempt to read something from it (the insanity of this rivals the
invention of atime) should be separated into a standalone issue.

{{{
class
FastSessionMiddleware(django.contrib.sessions.middleware.SessionMiddleware):
def process_request(self, request):
if is_api_request(request) and settings.SESSION_COOKIE_NAME not in
request.COOKIES:
return

super(FastSessionMiddleware, self).process_request(request)

def process_response(self, request, response):
if is_api_request(request):
return response

return super(FastSessionMiddleware,
self).process_response(request, response)

class
FastAuthenticationMiddleware(django.contrib.auth.middleware.AuthenticationMiddleware):
def process_request(self, request):
if hasattr(request, "session"):
return super(FastAuthenticationMiddleware,
self).process_request(request)

request.user = django.contrib.auth.models.AnonymousUser()

assert not
hasattr(django.contrib.auth.middleware.AuthenticationMiddleware,
"process_response")

def fast_login(request, user, remember=False):
if not hasattr(request, "session"):
assert not remember
request.user = user
return

auth.login(request, user)

request.session.set_expiry(None if remember else 0)

# also related, would appreciate this controlled by a settings item:
django.contrib.auth.signals.user_logged_in.disconnect(django.contrib.auth.models.update_last_login)
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:3>

Django

unread,
Sep 14, 2015, 12:57:19 PM9/14/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by timgraham):

Could you sketch out your idea for translating your code into a generic
patch for Django?

We try to include features in Django that meet the 80% use case test. If
this can be done outside of Django and doesn't meet that criteria, maybe
it's best to leave it like that. I suggested to write to the mailing list
to help gauge if it's a common issue faced by other developers.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:4>

Django

unread,
Sep 15, 2015, 11:00:55 AM9/15/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by rdaysky):

The changes don’t look profound to me.

Firstly, contrib.auth has only a few references to request.session, none
of which are essential. Just putting an if hasattr(request, "session")
prior to such code will do. This makes, for example, auth.login only set a
non-persistent request.user, but additional functionality such as emitting
a signal is preserved.

Secondly, when session middleware is used for some requests, but not
others (e. g. using the Django admin on an otherwise API-only site), there
should be a way to login a user without touching the session. Currently,
request.user = get_whatever_user() works just fine, but using an
officially supported function would be better, something like
auth.login(request, user_I_just_found_in_the_DB,
persist_in_session=False). Would also have the benefit of not having to
bother with the concept of authentication backends, not to mention being
much more future-proof in case some new logic appears in auth.login.

Finally, the session DB backend seems to go out of its way to ensure a
32-character base-36 session key is unique. That’s about 160 bits, same
length as SHA-1 output! If everyone in the world started generating a key
each millisecond, with 99.999995% probability no-one would have a
collision within a million years. Just generate a random key and store the
record in process_response, which gives the option of not storing it at
all in case no data got written to the session (currently a write request
is always performed on first access, even if that’s read access).

As for how common the issue is — it’s primarily a performance issue.
Django, being the core library that it is, should have as high performance
as possible out of the box, and removing unnecessary DB write queries that
it currently issues in its default configuration (the other culprit is
update_last_login) should be high on the list.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:5>

Django

unread,
Sep 15, 2015, 12:19:35 PM9/15/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by collinanderson):

Hi,

"currently a write request is always performed on first access, even if

that’s read access" -- I think this is a bug that's been recently fixed in
1.8.4 and 1.7.10:

"The SessionMiddleware has been modified to no longer create empty session
records."

What version of django are you seeing this behavior?

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:6>

Django

unread,
Sep 15, 2015, 4:03:53 PM9/15/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by rdaysky):

Good to know. That does alleviate much of the concern.

Still, some cleanup of contrib.auth, nothing major, would be nice, such as
getting the aforementioned official auth.login(persist=False) function.

In general, much of Django is built around the traditional, pre-REST, pre-
AJAX ways and interaction of Django components follows corresponding
workflows. Nothing wrong with that, but as a result some parts of Django
got too tightly coupled so when one thing becomes unnecessary, getting
other things in isolation can be cumbersome.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:7>

Django

unread,
Sep 15, 2015, 4:16:21 PM9/15/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: new
Component: contrib.auth | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by collinanderson):

Would it work to just set request.user = user and not use login() at all?
It sounds like you want to manually attach a user to a request without
using django at all.

It's also pretty easy to swap in your own subclass of the middleware like
shown above, without needing to modify django.

Also, django handles the session and user lazily, so if request.SESSION is
not accessed, the session table is never queried, and if request.user is
never accessed, then the user table won't be automatically queried, and
request.SESSION won't be accessed. They're only queried as needed.

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:8>

Django

unread,
Sep 22, 2015, 10:36:03 AM9/22/15
to django-...@googlegroups.com
#25392: Allow contrib.auth to work without sessions
------------------------------+--------------------------------------
Reporter: rdaysky | Owner: nobody
Type: New feature | Status: closed
Component: contrib.auth | Version: master
Severity: Normal | Resolution: needsinfo
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------
Changes (by timgraham):

* status: new => closed
* resolution: => needsinfo


Comment:

rdaysky (or anyone else interested in this), if you have a patch to share,
please send a pull request and reopen this ticket. Absent that, it's
unclear to me what particular changes should be done (especially given
Collin's comments about simply not using Django's built in functionality
and building something that matches your needs).

--
Ticket URL: <https://code.djangoproject.com/ticket/25392#comment:9>

Reply all
Reply to author
Forward
0 new messages