Sessions

About

Sessions provide a place to persist data in web applications, Beaker’s session system simplifies session implementation details by providing WSGI middleware that handles them.

All cookies are signed with an HMAC signature to prevent tampering by the client.

Lazy-Loading

Only when a session object is actually accessed will the session be loaded from the file-system, preventing performance hits on pages that don’t use the session.

Using

The session object provided by Beaker’s SessionMiddleware implements a dict-style interface with a few additional object methods. Once the SessionMiddleware is in place, a session object will be made available as beaker.session in the WSGI environ.

When a session is created on the backend, a cookie is placed in the response to the client.

Getting data out of the session:

myvar = session['somekey']

Testing for a value:

logged_in = 'user_id' in session

Adding data to the session:

session['name'] = 'Fred Smith'

Complete example using a basic WSGI app with sessions:

from beaker.middleware import SessionMiddleware

def simple_app(environ, start_response):
    # Get the session object from the environ
    session = environ['beaker.session']

    # Check to see if a value is in the session
    user = 'logged_in' in session

    # Set some other session variable
    session['user_id'] = 10

    start_response('200 OK', [('Content-type', 'text/plain')])
    return ['User is logged in: %s' % user]

# Configure the SessionMiddleware
session_opts = {
    'session.type': 'file',
    'session.cookie_expires': True,
}
wsgi_app = SessionMiddleware(simple_app, session_opts)

Now wsgi_app is a replacement of original application simple_app. You should specify it as a request handler in your WSGI configuration file.

Note

This example does not actually save the session for the next request. Adding the save() call explained below is required, or having the session set to auto-save.

Session Attributes / Keys

Sessions have several special attributes that can be used as needed by an application.

  • id - Unique 40 char SHA-generated session ID (by default this is uuid4).
  • last_accessed - The last time the session was accessed before the current access, if save_accessed_time is true; the last time it was modified if false; will be None if the session was just made

There’s several special session keys populated as well:

  • _accessed_time - When the session was loaded if save_accessed_time is true; when it was last written if false
  • _creation_time - When the session was created

Saving

Sessions can be saved using the save() method on the session object:

session.save()

Warning

Beaker relies on Python’s pickle module to pickle data objects for storage in the session. Objects that cannot be pickled should not be stored in the session. It’s suggested to switch to json data_serializer to avoid possible security issues with pickle.

This flags a session to be saved, and it will be stored on the chosen back-end at the end of the request.

Warning

When using the memory backend, session will only be valid for the process that created it and will be lost when process is restarted. It is usually suggested to only use the memory backend for development and not for production.

If it’s necessary to immediately save the session to the back-end, the persist() method should be used:

session.persist()

This is not usually the case however, as a session generally should not be saved should something catastrophic happen during a request.

Order Matters: When using the Beaker middleware, you must call save before the headers are sent to the client. Since Beaker’s middleware watches for when the start_response function is called to know that it should add its cookie header, the session must be saved before it is called.

Keep in mind that Response objects in popular frameworks (WebOb, Werkzeug, etc.) call start_response immediately, so if you are using one of those objects to handle your Response, you must call .save() before the Response object is called:

# this would apply to WebOb and possibly others too
from werkzeug.wrappers import Response

# this will work
def sessions_work(environ, start_response):
    environ['beaker.session']['count'] += 1
    resp = Response('hello')
    environ['beaker.session'].save()
    return resp(environ, start_response)

# this will not work
def sessions_broken(environ, start_response):
    environ['beaker.session']['count'] += 1
    resp = Response('hello')
    retval = resp(environ, start_response)
    environ['beaker.session'].save()
    return retval

Auto-save

Saves can be done automatically by setting the auto configuration option for sessions. When set, calling the save() method is no longer required, and the session will be saved automatically anytime it is accessed during a request.

Deleting

Calling the delete() method deletes the session from the back-end storage and sends an expiration on the cookie requesting the browser to clear it:

session.delete()

This should be used at the end of a request when the session should be deleted and will not be used further in the request.

If a session should be invalidated, and a new session created and used during the request, the invalidate() method should be used:

session.invalidate()

Removing Expired/Old Sessions

Beaker does not automatically delete expired or old cookies on any of its back-ends. This task is left up to the developer based on how sessions are being used, and on what back-end.

The database backend records the last accessed time as a column in the database so a script could be run to delete session rows in the database that haven’t been used in a long time.

When using the file-based sessions, a script could run to remove files that haven’t been touched in a long time, for example (in the session’s data dir):

find . -type f -mtime +3 -print -exec rm {} \;