



  

django-session-security 2.5.1 documentation

 [django-session-security](index.html#document-index) 

 latest

 

* [Quick setup](index.html#document-quick)

* [Full documentation](index.html#document-full)

	+ [Settings](index.html#module-session_security.settings)

	+ [Middleware](index.html#module-session_security.middleware)

	+ [Utils](index.html#module-session_security.utils)

	+ [Urls](index.html#module-session_security.urls)

	+ [Views](index.html#module-session_security.views)

	+ [Templates](index.html#templates)

	+ [Static files](index.html#static-files)

* [How to trigger activity programaticaly in JS ?](index.html#document-faq)

* [How to disable the “Are you sure you want to leave this page?” warning ?](index.html#how-to-disable-the-are-you-sure-you-want-to-leave-this-page-warning)

[django-session-security](index.html#document-index)

* [Docs](index.html#document-index) »

* django-session-security 2.5.1 documentation

* [Edit on GitHub](https://github.com/yourlabs/django-session-security/blob/stable/2.x.x/docs/source/index.rst)

---

Welcome to django-session-security’s documentation![¶](#welcome-to-django-session-security-s-documentation "Permalink to this headline")

========================================================================================================================================

![https://secure.travis-ci.org/yourlabs/django-session-security.png?branch=master](https://secure.travis-ci.org/yourlabs/django-session-security.png?branch=master)

This app provides a mechanism to logout inactive authenticated users. An

inactive browser should be logged out automatically if the user left his

workstation, to protect sensitive data that may be displayed in the browser. It

may be useful for CRMs, intranets, and such projects.

For example, if the user leaves for a coffee break, this app can force logout

after say 5 minutes of inactivity.

Why not just set the session to expire after X minutes ?[¶](#why-not-just-set-the-session-to-expire-after-x-minutes "Permalink to this headline")

-------------------------------------------------------------------------------------------------------------------------------------------------

Or “Why does this app even exist” ? Here are the reasons:

* if the user session expires before the user is done reading a page: he will

have to login again.

* if the user session expires before the user is done filling a form: his work

will be lost, and he will have to login again, and probably yell at you, dear

django dev ... at least I know I would !

This app allows to short circuit those limitations in session expiry.

How does it work ?[¶](#how-does-it-work "Permalink to this headline")

---------------------------------------------------------------------

When the user loads a page, SessionSecurity middleware will set the last

activity to now. The last activity is stored as datetime

in `request.session['\_session\_security']`. To avoid having the middleware

update that last activity datetime for a URL, add the url to

`settings.SESSION\_SECURITY\_PASSIVE\_URLS`.

When the user moves mouse, click, scroll or press a key, SessionSecurity will

save the DateTime as a JavaScript attribute. It will send the number of seconds

since when the last user activity was recorded to PingView, next time it should

ping.

First, a warning should be shown after `settings.SESSION\_SECURITY\_WARN\_AFTER`

seconds. The warning displays a text like “Your session is about to expire,

move the mouse to extend it”.

Before displaying this warning, SessionSecurity will upload the time since the

last client-side activity was recorded. The middleware will take it if it is

shorter than what it already has - ie. another more recent activity was

detected in another browser tab. The PingView will respond with the number of

seconds since the last activity - all browser tab included.

If there was no other, more recent, activity recorded by the server: it will

show the warning. Otherwise it will update the last activity in javascript from

the PingView response.

Same goes to expire after `settings.SESSION\_SECURITY\_EXPIRE\_AFTER` seconds.

Javascript will first make an ajax request to PingView to ensure that another

more recent activity was not detected anywhere else - in any other browser tab.

Requirements[¶](#requirements "Permalink to this headline")

-----------------------------------------------------------

* Python 2.7 or 3.5+

* jQuery 1.7+

* Django 1.8 to 2.0

* django.contrib.staticfiles or #YoYo

Resources[¶](#resources "Permalink to this headline")

-----------------------------------------------------

You could subscribe to the mailing list ask questions or just be informed of

package updates.

* [Git graciously hosted](https://github.com/yourlabs/django-session-security/) by [GitHub](http://github.com),

* [Documentation graciously hosted](http://django-session-security.rtfd.org) by [RTFD](http://rtfd.org),

* [Package graciously hosted](http://pypi.python.org/pypi/django-session-security/) by [PyPi](http://pypi.python.org/pypi),

* [Mailing list graciously hosted](http://groups.google.com/group/yourlabs) by [Google](http://groups.google.com)

* For **Security** issues, please contact [yourlabs-security@googlegroups.com](mailto:yourlabs-security%40googlegroups.com)

* [Continuous integration graciously hosted](http://travis-ci.org/yourlabs/django-session-security) by [Travis-ci](http://travis-ci.org)

Contents:

### Quick setup[¶](#quick-setup "Permalink to this headline")

The purpose of this documentation is to get you started as fast as possible,

because your time matters and you probably have other things to worry about.

Install the package:

```

pip install django-session-security

# or the development version

pip install -e git+git://github.com/yourlabs/django-session-security.git#egg=django-session-security

```

For static file service, add to `settings.INSTALLED\_APPS`:

```

'session\_security',

```

Add to `settings.MIDDLEWARE\_CLASSES`, **after** django’s AuthenticationMiddleware:

```

'session\_security.middleware.SessionSecurityMiddleware',

```

Ensure settings.TEMPLATE\_CONTEXT\_PROCESSORS has:

```

'django.core.context\_processors.request'

```

Add to urls:

```

url(r'session\_security/', include('session\_security.urls')),

```

At this point, we’re going to assume that you have [django.contrib.staticfiles](https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/) working.

This means that [static files are automatically served with runserver](https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#runserver),

and that you have to run [collectstatic when using another server](https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#collectstatic)

(fastcgi, uwsgi, and whatnot). If you don’t use django.contrib.staticfiles,

then you’re on your own to manage staticfiles.

After jQuery, add to your base template:

```

{% include 'session\_security/all.html' %}

```

### Full documentation[¶](#full-documentation "Permalink to this headline")

#### Settings[¶](#module-session_security.settings "Permalink to this headline")

Settings for django-session-security.

WARN\_AFTER

Time (in seconds) before the user should be warned that is session will

expire because of inactivity. Default 540. Overridable in

`settings.SESSION\_SECURITY\_WARN\_AFTER`.

EXPIRE\_AFTER

Time (in seconds) before the user should be logged out if inactive. Default

is 600. Overridable in `settings.SESSION\_SECURITY\_EXPIRE\_AFTER`.

PASSIVE\_URLS

List of urls that should be ignored by the middleware. For example the ping

ajax request of session\_security is made without user intervention, as such

it should not be used to update the user’s last activity datetime.

Overridable in `settings.SESSION\_SECURITY\_PASSIVE\_URLS`.

PASSIVE\_URL\_NAMES

Same as PASSIVE\_URLS, but takes Django URL names instead of a path. This

is useful in case path names change, or contain parameterized values, and

thus cannot be described statically. NOTE: currently namespaces are not

handled. Overridable in `settings.SESSION\_SECURITY\_PASSIVE\_URL\_NAMES`.

SESSION\_SECURITY\_INSECURE

Set this to True in your settings if you want the project to run without

having to set SESSION\_EXPIRE\_AT\_BROWSER\_CLOSE=True, which you should

because it makes no sense to use this app with

`SESSION\_EXPIRE\_AT\_BROWSER\_CLOSE` to False.

#### Middleware[¶](#module-session_security.middleware "Permalink to this headline")

SessionSecurityMiddleware is the heart of the security that this application

attemps to provide.

To install this middleware, add to your `settings.MIDDLEWARE\_CLASSES`:

```

'session\_security.middleware.SessionSecurityMiddleware'

```

Make sure that it is placed **after** authentication middlewares.

*class* `session_security.middleware.``SessionSecurityMiddleware`(*get\_response=None*)[[source]](_modules/session_security/middleware.html#SessionSecurityMiddleware)[¶](#session_security.middleware.SessionSecurityMiddleware "Permalink to this definition")

In charge of maintaining the real ‘last activity’ time, and log out the

user if appropriate.

`get_expire_seconds`(*request*)[[source]](_modules/session_security/middleware.html#SessionSecurityMiddleware.get_expire_seconds)[¶](#session_security.middleware.SessionSecurityMiddleware.get_expire_seconds "Permalink to this definition")

Return time (in seconds) before the user should be logged out.

`is_passive_request`(*request*)[[source]](_modules/session_security/middleware.html#SessionSecurityMiddleware.is_passive_request)[¶](#session_security.middleware.SessionSecurityMiddleware.is_passive_request "Permalink to this definition")

Should we skip activity update on this URL/View.

`process_request`(*request*)[[source]](_modules/session_security/middleware.html#SessionSecurityMiddleware.process_request)[¶](#session_security.middleware.SessionSecurityMiddleware.process_request "Permalink to this definition")

Update last activity time or logout.

`update_last_activity`(*request*, *now*)[[source]](_modules/session_security/middleware.html#SessionSecurityMiddleware.update_last_activity)[¶](#session_security.middleware.SessionSecurityMiddleware.update_last_activity "Permalink to this definition")

If `request.GET['idleFor']` is set, check if it refers to a more

recent activity than `request.session['\_session\_security']` and

update it in this case.

#### Utils[¶](#module-session_security.utils "Permalink to this headline")

Helpers to support json encoding of session data

`session_security.utils.``get_last_activity`(*session*)[[source]](_modules/session_security/utils.html#get_last_activity)[¶](#session_security.utils.get_last_activity "Permalink to this definition")

Get the last activity datetime string from the session and return the

python datetime object.

`session_security.utils.``set_last_activity`(*session*, *dt*)[[source]](_modules/session_security/utils.html#set_last_activity)[¶](#session_security.utils.set_last_activity "Permalink to this definition")

Set the last activity datetime as a string in the session.

#### Urls[¶](#module-session_security.urls "Permalink to this headline")

One url meant to be used by JavaScript.

session\_security\_ping

Connects the PingView.

To install this url, include it in `urlpatterns` definition in `urls.py`,

ie:

```

urlpatterns = patterns('',

    # ....

    url(r'session\_security/', include('session\_security.urls')),

    # ....

)

```

#### Views[¶](#module-session_security.views "Permalink to this headline")

One view method for AJAX requests by SessionSecurity objects.

*class* `session_security.views.``PingView`(*\*\*kwargs*)[[source]](_modules/session_security/views.html#PingView)[¶](#session_security.views.PingView "Permalink to this definition")

This view is just in charge of returning the number of seconds since the

‘real last activity’ that is maintained in the session by the middleware.

Constructor. Called in the URLconf; can contain helpful extra

keyword arguments, and other things.

#### Templates[¶](#templates "Permalink to this headline")

##### session\_security/dialog.html[¶](#session-security-dialog-html "Permalink to this headline")

```

{% load i18n %}

<div id="session\_security\_warning" class="session\_security" style="display:none" aria-hidden="true" role="dialog">

    <div class="session\_security\_overlay"></div>

    <div class="session\_security\_modal" role="document" tabindex="-1">

        <h3>{% trans 'Your session is about to expire' %}</h3>

        <p>{% trans 'Click or type to extend your session.' %}</p>

    </div>

</div>

```

##### session\_security/all.html[¶](#session-security-all-html "Permalink to this headline")

```

{% comment %}

This demonstrates how to setup session security client side stuff on your own.

It provides sensible defaults so you could start with just::

 {% include 'session\_security/all.html' %}

{% endcomment %}

{% load session\_security\_tags %}

{% load i18n l10n %}

{% load static from staticfiles %}

{# If the user is not authenticated then there is no session to secure ! #}

{% if request.user.is\_authenticated %}

 {# The modal dialog stylesheet, it's pretty light so it should be easy to hack #}

 <link rel="stylesheet" type="text/css" href="{% static 'session\_security/style.css' %}">

 {# Include the template that actually contains the modal dialog #}

 {% include 'session\_security/dialog.html' %}

 {# Load SessionSecurity javascript 'class', jquery should be loaded - by you - at this point #}

 <script type="text/javascript" src="{% static 'session\_security/script.js' %}"></script>

 {# Bootstrap a SessionSecurity instance as the sessionSecurity global variable #}

 {% localize off %}

 <script type="text/javascript">

 var sessionSecurity = new yourlabs.SessionSecurity({

 pingUrl: '{% url 'session\_security\_ping' %}',

 warnAfter: {{ request|warn\_after|unlocalize }},

 expireAfter: {{ request|expire\_after|unlocalize }},

 confirmFormDiscard: "{% trans 'You have unsaved changes in a form of this page.' %}"

 });

 </script>

 {% endlocalize %} 

{% endif %}

```

#### Static files[¶](#static-files "Permalink to this headline")

##### session\_security/script.js[¶](#session-security-script-js "Permalink to this headline")

Read the [script documentation](_static/script.html)

##### session\_security/style.css[¶](#session-security-style-css "Permalink to this headline")

```

/* credit: http://www.csslab.cl/2008/01/30/ventana-modal-solo-con-css/ */

.session_security_overlay {

    position: fixed;

    top: 0;

    left: 0;

    width: 100%;

    height: 100%;

    background: #000;

    z-index:1001;

    opacity:.75;

    -moz-opacity: 0.75;

    filter: alpha(opacity=75);

}

.session_security_modal {

    position: fixed;

    top: 25%;

    left: 25%;

    width: 50%;

    padding: 16px;

    background: #fff;

    color: #333;

    z-index:1002;

    overflow: auto;

    text-align: center;

}

```

### How to trigger activity programaticaly in JS ?[¶](#how-to-trigger-activity-programaticaly-in-js "Permalink to this headline")

Call sessionSecurity.activity()

<http://django-session-security.readthedocs.org/en/stable-2.x.x/\_static/script.html#section-11>

every time you want to programaticaly trigger an activity.

### How to disable the “Are you sure you want to leave this page?” warning ?[¶](#how-to-disable-the-are-you-sure-you-want-to-leave-this-page-warning "Permalink to this headline")

Include the JavaScript variable `sessionSecurity.confirmFormDiscard = undefined;` somewhere in your project *after* the plugin’s JS. For example:

```

<!-- base.html -->

...

{% include 'session_security/all.html' %}

<script>

    sessionSecurity.confirmFormDiscard = undefined;

</script>

...

```

Indices and tables[¶](#indices-and-tables "Permalink to this headline")

=======================================================================

* [Index](genindex.html)

* [Module Index](py-modindex.html)

* [Search Page](search.html)





script.js

script.js

=========

[#](#section-0)

Use 'yourlabs' as namespace.

```

if (window.yourlabs == undefined) window.yourlabs = {};

```

[#](#section-1)

Session security constructor. These are the required options:

* pingUrl: url to ping with last activity in this tab to get global last

 activity time,

* warnAfter: number of seconds of inactivity before warning,

* expireAfter: number of seconds of inactivity before expiring the session.

Optional options:

* confirmFormDiscard: message that will be shown when the user tries to

 leave a page with unsaved form data. Setting this will enable an

 onbeforeunload handler that doesn't block expire().

* events: a list of event types to watch for activity updates.

* returnToUrl: a url to redirect users to expired sessions to. If this is not defined we just reload the page

```

yourlabs.SessionSecurity = function(options) {

```

[#](#section-2)

**HTML element** that should show to warn the user that his session will

expire.

```

    this.$warning = $('#session\_security\_warning');

```

[#](#section-3)

Last recorded activity datetime.

```

    this.lastActivity = new Date();

```

[#](#section-4)

Events that would trigger an activity

```

    this.events = ['mousemove', 'scroll', 'keyup', 'click', 'touchstart', 'touchend', 'touchmove'];

```

[#](#section-5)

Merge the options dict here.

```

    $.extend(this, options);

```

[#](#section-6)

Bind activity events to update this.lastActivity.

```

    var $document = $(document);

    for(var i=0; i<this.events.length; i++) {

        if ($document[this.events[i]]) {

            $document[this.events[i]]($.proxy(this.activity, this));

        }

    }

```

[#](#section-7)

Initialize timers.

```

    this.apply()

    if (this.confirmFormDiscard) {

        window.onbeforeunload = $.proxy(this.onbeforeunload, this);

        $document.on('change', ':input', $.proxy(this.formChange, this));

        $document.on('submit', 'form', $.proxy(this.formClean, this));

        $document.on('reset', 'form', $.proxy(this.formClean, this));

    }

}

yourlabs.SessionSecurity.prototype = {

```

[#](#section-8)

Called when there has been no activity for more than expireAfter

seconds.

```

    expire: function() {

        this.expired = true;

        if (this.returnToUrl !== undefined) {

            window.location.href = this.returnToUrl;

        }

        else {

            window.location.reload();

        }

    },

```

[#](#section-9)

Called when there has been no activity for more than warnAfter

seconds.

```

    showWarning: function() {

        this.$warning.fadeIn('slow');

        this.$warning.attr('aria-hidden', 'false');

        $('.session\_security\_modal').focus();

    },

```

[#](#section-10)

Called to hide the warning, for example if there has been activity on

the server side - in another browser tab.

```

    hideWarning: function() {

        this.$warning.hide();

        this.$warning.attr('aria-hidden', 'true');

    },

```

[#](#section-11)

Called by click, scroll, mousemove, keyup, touchstart, touchend, touchmove

```

    activity: function() {

        var now = new Date();

        if (now - this.lastActivity < 1000)

```

[#](#section-12)

Throttle these checks to once per second

```

            return;

        this.lastActivity = now;

        if (this.$warning.is(':visible')) {

```

[#](#section-13)

Inform the server that the user came back manually, this should

block other browser tabs from expiring.

```

            this.ping();

        }

        this.hideWarning();

    },

```

[#](#section-14)

Hit the PingView with the number of seconds since last activity.

```

    ping: function() {

        var idleFor = Math.floor((new Date() - this.lastActivity) / 1000);

        $.ajax(this.pingUrl, {

            data: {idleFor: idleFor},

            cache: false,

            success: $.proxy(this.pong, this),

```

[#](#section-15)

In case of network error, we still want to hide potentially

confidential data !!

```

            error: $.proxy(this.apply, this),

            dataType: 'json',

            type: 'get'

        });

    },

```

[#](#section-16)

Callback to process PingView response.

```

    pong: function(data) {

        if (data == 'logout') return this.expire();

        this.lastActivity = new Date();

        this.lastActivity.setSeconds(this.lastActivity.getSeconds() - data);

        this.apply();

    },

```

[#](#section-17)

Apply warning or expiry, setup next ping

```

    apply: function() {

```

[#](#section-18)

Cancel timeout if any, since we're going to make our own

```

        clearTimeout(this.timeout);

        var idleFor = Math.floor((new Date() - this.lastActivity) / 1000);

        if (idleFor >= this.expireAfter) {

            return this.expire();

        } else if (idleFor >= this.warnAfter) {

            this.showWarning();

            nextPing = this.expireAfter - idleFor;

        } else {

            this.hideWarning();

            nextPing = this.warnAfter - idleFor;

        }

        this.timeout = setTimeout($.proxy(this.ping, this), nextPing \* 1000);

    },

```

[#](#section-19)

onbeforeunload handler.

```

    onbeforeunload: function(e) {

        if ($('form[data-dirty]').length && !this.expired) {

            return this.confirmFormDiscard;

        }

    },

```

[#](#section-20)

When an input change, set data-dirty attribute on its form.

```

    formChange: function(e) {

        $(e.target).closest('form').attr('data-dirty', true);

    },

```

[#](#section-21)

When a form is submitted or resetted, unset data-dirty attribute.

```

    formClean: function(e) {

        $(e.target).removeAttr('data-dirty');

    }

}

```

