Skip to main content

Cookies and Session Management for Sanic

Project description


Software License Build Status Code style: black Downloads Monthly Downloads

Sanic Cookies

Much of the code here is borrowed from sanic_session.

I wanted to make some changes that would break a big part of sanic_session's API, so I decided to create this repo instead.

Sanic cookies supports both client side and server side cookies.

Main deviations from sanic_session are

  • Interfaces are only responsible for reading/writing the SessionDict:

    Session management logic is handled by the Session object

  • No race conditions:

    By using:

    async with request['session']:
        request['session']['foo'] = 'bar'

    instead of:

    request['session']['foo'] = 'bar'

    It is still however possible to use the session_dict without a context manager, but it will raise some warnings, unless it's explicitly turned off (warn_lock=False)


    The locking mechanism used here only keeps track of locks on a thread-level, which means, an application that is horizontally scaled or one that runs on more than one process won't fully benefit from the locking mechanism that sanic-cookies currently has in place and might encounter some race conditions. I have plans to introduce a distributed locking mechanism. Probably using something like: Aioredlock. But for now, you should know that the locking mechanism that is currently in place will not work in a multi-process environment.

  • A simpler implementation of SessionDict that helps me sleep in peace at night. (Probably less performant)

  • In memory interface schedules cleanup to avoid running out of memory

  • Encrypted client side cookie interface

  • Ability to add more than one interface to the same session

  • Authenticated Session implementation

Setup ⚙️

$ pip install sanic_cookies

Quick Start

from sanic_cookies import Session, InMemory
from sanic import Sanic

app = Sanic()
Session(app, master_interface=InMemory())

async def handler(request):
    async with request['session'] as sess:
        sess['foo'] = 'bar'


Running multiple interfaces

from sanic_cookies import Session, InMemory, Aioredis
from sanic import Sanic

inmem = InMemory()
aioredis = AioRedis(aioredis_pool_instance)
app = Sanic()
sess = Session(app, master_interface=inmem, session_name='my_1st_sess')

async def index(request):
    async with request['my_1st_session'] as sess:
        sess['foo'] = 'bar'
        # At this point 'foo' = 'bar' is written both to the inmemory
        # interface and the aioredis interface

    async with request['my_1st_session'] as sess:
        # When reading, your session will always read from the "master_interface"
        # In that case it's the inmem interface
        assert sess['foo'] == 'bar'
    # Such pattern can be useful in many cases 
    # e.g. you want to share your session information with an analytics team

Running multiple sessions

from sanic_cookies import Session, AuthSession, InMemory, InCookieEncrypted, AioRedis
from sanic import Sanic

inmem = InMemory()
aioredis = Aioredis(aioredis_pool_instance)
incookie = InCookieEncrypted(b'fernetsecretkey')

app = Sanic()

incookie_session = Session(

generic_session = Session(

auth_session = AuthSession(

# for production (HTTPs) set `secure=True` in your auth_session,
# but this will fail in local development

async def index(request):
    async with request['incookie_session'] as sess:
        sess['foo'] = 'bar'

    async with request['session'] as sess:
        sess['bar'] = 'baz'

    async with request['auth_session'] as sess:
        sess['baz'] = 'foo'


Following up on the previous example:

from sanic_cookies import login_required

async def login(request):
    # 1. User verification logic

    # both will work (Whatever is json serializble will)
    # If you want to pickle an object simply change the default
    # encoder&decoder in the interfaces plugged in to your AuthSession
    authorized_user = 123 
    authorized_user = {'user_id': 123, 'email': 'foo@bar.baz'}

    # 2. Login user

    # Here we access the session object
    # (not the session dict that is accessible from the request) from the app
    await, authorized_user)

    # 3. Use the session dict safely and exclusively for the logged in user

    async with request['auth_session'] as sess:
        sess['foo'] = 'bar'
        current_user = sess['current_user']
    assert current_user == await

async def logout(request):
    async with request['auth_session'] as sess:
        assert sess['foo'] == 'bar'  # From before

    await  # Resets the session

    async with request['auth_session'] as sess:
        assert sess.get('foo') is None  # should never fail
        assert sess.get('current_user') is None  # should never fail

async def protected(request):
    assert await is not None  # should never fail

Interfaces available

  1. In memory

    from sanic_cookies import Session, InMemory
    from sanic import Sanic
    interface = InMemory()
    app = Sanic()
    Session(app, master_interface=interface)
    # You can skip this part if you don't want scheduled interface cleanup
    def init_inmemory(app, loop):
    def kill_inmemory(app, loop):
    async def handler(request):
        async with request['session'] as sess:
            sess['foo'] = 'bar'
  2. Aioredis

    from aioredis import Aioredis
    from sanic_cookies import Aioredis as AioredisInterface
    from sanic import Sanic
    app = Sanic()
    aioredis_pool_instance = Aioredis()
    aioredis = AioredisInterface(aioredis_pool_instance)
    Session(app, master_interface=interface)
    async def handler(request):
        async with request['session'] as sess:
            sess['foo'] = 'bar'
  3. Encrypted in-cookie (using the amazing cryptography.Fernet library)

    i. Open a Python terminal and generate a new Fernet key:

    >>> from cryptography.fernet import Fernet
    >>> SESSION_KEY = Fernet.generate_key()
    >>> print(SESSION_KEY)
    b'copy me to your sanic app and keep me really secure'

    ii. Write your app

    from sanic import Sanic
    from sanic_cookies import Session, InCookieEncrypted
    app = Sanic()
    app.config.SESSION_KEY = SESSION_KEY
    async def handler(request):
        async with request['session'] as sess:
            sess['foo'] = 'bar'
  4. Gino-AsyncPG (Postgres 9.5+):

    i. Manually create a table:

        created_at timestamp without time zone NOT NULL,
        expires_at timestamp without time zone,
        sid character varying,
        val character varying,
        CONSTRAINT sessions_pkey PRIMARY KEY (sid)

    ii. Add the interface:

    from sanic import Sanic
    from gino.ext.sanic import Gino
    from sanic_cookies import GinoAsyncPG
    from something_secure import DB_SETTINGS
    app = Sanic()
    db = Gino()
    interface = GinoAsyncPG(client=db)
    auth_session = AuthSession(app, master_interface=interface)
    if __name__ == '__main__':'', port='8080')

Sessions available

  1. Session (A generic session interface)
  2. AuthSession (A session interface with login_user, logout_user, current_user logic)

Other pluggable parts

  1. Encoders and Decoders (Default to ujson)
  2. SID factory (Default to uuid.uuid4)
  3. Session dict implementation

Contact 📧

I currently work as a freelance software devloper. Like my work and got a gig for me?

Want to hire me fulltime? Send me an email @

Buy me a coffee ☕

Bitcoin: 3NmywNKr1Lzo8gyNXFUnzvboziACpEa31z

Ethereum: 0x1E1400C31Cd813685FE0f6D29E0F91c1Da4675aE

Bitcoin Cash: qqzn7rsav6hr3zqcp4829s48hvsvjat4zq7j42wkxd

Litecoin: MB5M3cE3jE4E8NwGCWoFjLvGqjDqPyyEJp


Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sanic_cookies-0.4.3.tar.gz (19.0 kB view hashes)

Uploaded source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page