4

I'm trying to implement a "remember me" checkbox into django's builtin LoginView, as suggested on this question, but even though I call set_expiry(0), the sessions still expire after SESSION_COOKIE_AGE, regardless of the cookie expire date (which is correctly set to 1969).

I'm using django 2.1.7 with python 3.7.2, and the only session-related settings on my settings.py is SESSION_COOKIE_AGE, which is set to 5 seconds for resting purposes.

Django seems to use a database backend as default. I'm using sqlite for development.

This is my view class:

class UserLoginView(LoginView):
    form_class = registration.UserLoginForm

    def form_valid(self, form):
        remember = form.data.get('remember_me', False)
        if remember:
            self.request.session.set_expiry(0)
        return super(UserLoginView, self).form_valid(form)

And this is the original LoginView form_valid method (being overriden above)

class LoginView(SuccessURLAllowedHostsMixin, FormView):

...

    def form_valid(self, form):
        """Security check complete. Log the user in."""
        auth_login(self.request, form.get_user())
        return HttpResponseRedirect(self.get_success_url())

...

As you noticed, I'm using a custom form_class. A very simple override of the default form:

class UserLoginForm(AuthenticationForm):
    remember_me = BooleanField(required=False)

If I use a debugger right after the set_expiry call, I can see that the sesion expiry age is still the default 5 seconds:

> /project/app/views/accounts.py(64)form_valid()
-> return super(UserLoginView, self).form_valid(form)
(Pdb) self.request.session.get_expiry_age()
5

I get similar results if I let the request complete and redirect, reach the next view and finally render a template where I have:

...

{{ request.session.get_expiry_age }}

...

The rendered result is also 5 (the current default).

Sure enough, after 5 seconds, if you refresh the page, django will take you back to the login screen.

What am I doing wrong here? It would be nice if someone could clarify what does "Web browser is closed" means here? https://docs.djangoproject.com/en/2.2/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase.set_expiry

phrfpeixoto
  • 191
  • 1
  • 2
  • 11

1 Answers1

3

TL; DR; Seems like Django does not offer support for infinite or truly undefined expiry session times. Set it to 30 days or greater if you need to extend its validity.

From Django documentation:

If value is 0, the user’s session cookie will expire when the user’s Web browser is closed.

Although it's not clear here, seems like setting the expiry time to 0 has a similar behavior when setting it to None: it will fallback to the default session expiry policy. The difference here is that when setting it to 0, we're also inferring that the session should be expired right after the user closes the browser. In both cases, SESSION_COOKIE_AGE works like a session max-age value.

I believe you could set a greater number to turn around this problem, for example, something equivalent to 100 years or more. My personal suggestion is to specify an expiry time of 30 days when the user checks the "remember me" field. When you specify a positive integer greater than zero, Django won't fallback to the SESSION_COOKIE_AGE setting.

If you're curious about why you're getting 5 seconds even after specifying an expiry of 0 seconds, here's the source code extracted from the get_expiry_age function:

if not expiry:   # Checks both None and 0 cases
    return settings.SESSION_COOKIE_AGE
if not isinstance(expiry, datetime):
    return expiry

Final considerations:

  • there's some room for improvements in the Django documentation
  • seems like refreshing a tab could also invalidate the session
  • OK, I can see why `get_expiry_age` returns the default value, but it still does not make a lot of sense to my why the session is in fact expiring, if It was set to 0 and I did not close the browser window. – phrfpeixoto Apr 04 '19 at 12:37