diff --git a/.travis.yml b/.travis.yml
index 84321a6..0f0d5d4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,7 @@ install:
 - pip install coveralls
 
 script:
-- pyflakes aiohttp_security tests
-- pep8 aiohttp_security tests
+- flake8 aiohttp_security tests
 - coverage run --source=aiohttp_security setup.py test
 
 after_success:
diff --git a/README.rst b/README.rst
index fa83e86..733574a 100644
--- a/README.rst
+++ b/README.rst
@@ -9,6 +9,14 @@ __ aiohttp_web_
 
 Usage
 -----
+To install type ``pip install aiohttp_security``.
+Launch ``make doc`` and see examples or look under **demo** directory for a
+sample project.
+
+Develop
+-------
+
+``pip install -r requirements-dev``
 
 
 License
diff --git a/aiohttp_security/cookies_identity.py b/aiohttp_security/cookies_identity.py
index 72ead90..807af34 100644
--- a/aiohttp_security/cookies_identity.py
+++ b/aiohttp_security/cookies_identity.py
@@ -1,4 +1,4 @@
-"""Identity polocy for storing info directly into HTTP cookie.
+"""Identity policy for storing info directly into HTTP cookie.
 
 Use mostly for demonstration purposes, SessionIdentityPolicy is much
 more handy.
diff --git a/aiohttp_security/session_identity.py b/aiohttp_security/session_identity.py
index ea2beac..5fdf55a 100644
--- a/aiohttp_security/session_identity.py
+++ b/aiohttp_security/session_identity.py
@@ -1,7 +1,7 @@
 """Identity policy for storing info into aiohttp_session session.
 
 aiohttp_session.setup() should be called on application initialization
-to conffigure aiohttp_session properly.
+to configure aiohttp_session properly.
 """
 
 import asyncio
diff --git a/demo/db.py b/demo/db.py
index 0fb875a..c315988 100644
--- a/demo/db.py
+++ b/demo/db.py
@@ -9,7 +9,6 @@ users = sa.Table(
     sa.Column('id', sa.Integer, nullable=False),
     sa.Column('login', sa.String(256), nullable=False),
     sa.Column('passwd', sa.String(256), nullable=False),
-    sa.Column('salt', sa.String(256), nullable=False),
     sa.Column('is_superuser', sa.Boolean, nullable=False,
               server_default='FALSE'),
     sa.Column('disabled', sa.Boolean, nullable=False,
diff --git a/demo/db_auth.py b/demo/db_auth.py
index f58f47e..bb9897c 100644
--- a/demo/db_auth.py
+++ b/demo/db_auth.py
@@ -1,7 +1,9 @@
 import asyncio
+
 import sqlalchemy as sa
 
 from aiohttp_security.abc import AbstractAuthorizationPolicy
+from passlib.hash import sha256_crypt
 
 from . import db
 
@@ -11,11 +13,11 @@ class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
         self.dbengine = dbengine
 
     @asyncio.coroutine
-    def authorized_user_id(self, identity):
+    def authorized_userid(self, identity):
         with (yield from self.dbengine) as conn:
-            where = [db.users.c.login == identity,
-                     not db.users.c.disabled]
-            query = db.users.count().where(sa.and_(*where))
+            where = sa.and_(db.users.c.login == identity,
+                            sa.not_(db.users.c.disabled))
+            query = db.users.count().where(where)
             ret = yield from conn.scalar(query)
             if ret:
                 return identity
@@ -24,12 +26,42 @@ class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
 
     @asyncio.coroutine
     def permits(self, identity, permission, context=None):
+        if identity is None:
+            return False
+
         with (yield from self.dbengine) as conn:
-            where = [db.users.c.login == identity,
-                     not db.users.c.disabled]
-        record = self.data.get(identity)
-        if record is not None:
-            # TODO: implement actual permission checker
-            if permission in record:
-                return True
-        return False
+            where = sa.and_(db.users.c.login == identity,
+                            sa.not_(db.users.c.disabled))
+            query = db.users.select().where(where)
+            ret = yield from conn.execute(query)
+            user = yield from ret.fetchone()
+            if user is not None:
+                user_id = user[0]
+                is_superuser = user[3]
+                if is_superuser:
+                    return True
+
+                where = db.permissions.c.user_id == user_id
+                query = db.permissions.select().where(where)
+                ret = yield from conn.execute(query)
+                result = yield from ret.fetchall()
+                if ret is not None:
+                    for record in result:
+                        if record.perm_name == permission:
+                            return True
+
+            return False
+
+
+@asyncio.coroutine
+def check_credentials(db_engine, username, password):
+    with (yield from db_engine) as conn:
+        where = sa.and_(db.users.c.login == username,
+                        sa.not_(db.users.c.disabled))
+        query = db.users.select().where(where)
+        ret = yield from conn.execute(query)
+        user = yield from ret.fetchone()
+        if user is not None:
+            hash = user[2]
+            return sha256_crypt.verify(password, hash)
+    return False
diff --git a/demo/handlers.py b/demo/handlers.py
index 313611e..3de2aed 100644
--- a/demo/handlers.py
+++ b/demo/handlers.py
@@ -3,53 +3,93 @@ import functools
 
 from aiohttp import web
 
-
 from aiohttp_security import remember, forget, authorized_userid, permits
 
+from .db_auth import check_credentials
+
 
 def require(permission):
     def wrapper(f):
         @asyncio.coroutine
         @functools.wraps(f)
         def wrapped(self, request):
-            has_perm = yield from permits(request)
+            has_perm = yield from permits(request, permission)
             if not has_perm:
-                raise web.HTTPForbidden()
+                message = 'User has no permission {}'.format(permission)
+                raise web.HTTPForbidden(body=message.encode())
             return (yield from f(self, request))
         return wrapped
     return wrapper
 
 
-class Web:
-    @require('public')
+class Web(object):
+    index_template = """
+<!doctype html>
+<head>
+</head>
+<body>
+<p>{message}</p>
+<form action="/login" method="post">
+  Login:
+  <input type="text" name="login">
+  Password:
+  <input type="password" name="password">
+  <input type="submit" value="Login">
+</form>
+<a href="/logout">Logout</a>
+</body>
+"""
+
     @asyncio.coroutine
     def index(self, request):
-        pass
+        username = yield from authorized_userid(request)
+        if username:
+            template = self.index_template.format(
+                message='Hello, {username}!'.format(username=username))
+        else:
+            template = self.index_template.format(message='You need to login')
+        response = web.Response(body=template.encode())
+        return response
 
-    @require('public')
     @asyncio.coroutine
     def login(self, request):
-        pass
+        response = web.HTTPFound('/')
+        form = yield from request.post()
+        login = form.get('login')
+        password = form.get('password')
+        db_engine = request.app.db_engine
+        if (yield from check_credentials(db_engine, login, password)):
+            yield from remember(request, response, login)
+            return response
 
-    @require('protected')
-    @asyncio.coroutine
-    def logout(self, request):
-        pass
+        return web.HTTPUnauthorized(
+            body=b'Invalid username/password combination')
 
     @require('public')
     @asyncio.coroutine
-    def public(self, request):
-        pass
+    def logout(self, request):
+        response = web.Response(body=b'You have been logged out')
+        yield from forget(request, response)
+        return response
+
+    @require('public')
+    @asyncio.coroutine
+    def internal_page(self, request):
+        response = web.Response(
+            body=b'This page is visible for all registered users')
+        return response
 
     @require('protected')
     @asyncio.coroutine
-    def protected(self, request):
-        pass
+    def protected_page(self, request):
+        response = web.Response(body=b'You are on protected page')
+        return response
 
-    @asyncio.coroutine
     def configure(self, app):
-        app.add_route('GET', '/', self.index, name='index')
-        app.add_route('POST', '/login', self.login, name='login')
-        app.add_route('POST', '/logout', self.logout, name='logout')
-        app.add_route('GET', '/public', self.public, name='public')
-        app.add_route('GET', '/protected', self.protected, name='protected')
+        router = app.router
+        router.add_route('GET', '/', self.index, name='index')
+        router.add_route('POST', '/login', self.login, name='login')
+        router.add_route('GET', '/logout', self.logout, name='logout')
+        router.add_route('GET', '/public', self.internal_page, name='public')
+        router.add_route('GET', '/protected', self.protected_page,
+                         name='protected')
diff --git a/demo/main.py b/demo/main.py
index 29b4ead..4fb3211 100644
--- a/demo/main.py
+++ b/demo/main.py
@@ -16,22 +16,23 @@ from demo.handlers import Web
 @asyncio.coroutine
 def init(loop):
     redis_pool = yield from create_pool(('localhost', 6379))
-    dbengine = yield from create_engine(user='aiohttp_security',
-                                        password='aiohttp_security',
-                                        database='aiohttp_security',
-                                        host='127.0.0.1')
+    db_engine = yield from create_engine(user='aiohttp_security',
+                                         password='aiohttp_security',
+                                         database='aiohttp_security',
+                                         host='127.0.0.1')
     app = web.Application(loop=loop)
+    app.db_engine = db_engine
     setup_session(app, RedisStorage(redis_pool))
     setup_security(app,
                    SessionIdentityPolicy(),
-                   DBAuthorizationPolicy(dbengine))
+                   DBAuthorizationPolicy(db_engine))
 
     web_handlers = Web()
-    yield from web_handlers.configure(app)
+    web_handlers.configure(app)
 
     handler = app.make_handler()
     srv = yield from loop.create_server(handler, '127.0.0.1', 8080)
-    print("Server started at http://127.0.0.1:8080")
+    print('Server started at http://127.0.0.1:8080')
     return srv, app, handler
 
 
@@ -54,3 +55,7 @@ def main():
         loop.run_forever()
     except KeyboardInterrupt:
         loop.run_until_complete((finalize(srv, app, handler)))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/demo/sql/init_db.sql b/demo/sql/init_db.sql
new file mode 100644
index 0000000..2a53f94
--- /dev/null
+++ b/demo/sql/init_db.sql
@@ -0,0 +1,5 @@
+CREATE USER aiohttp_security WITH PASSWORD 'aiohttp_security';
+DROP DATABASE IF EXISTS aiohttp_security;
+CREATE DATABASE aiohttp_security;
+ALTER DATABASE aiohttp_security OWNER TO aiohttp_security;
+GRANT ALL PRIVILEGES ON DATABASE aiohttp_security TO aiohttp_security;
diff --git a/demo/sql/sample_data.sql b/demo/sql/sample_data.sql
new file mode 100644
index 0000000..3a4a37e
--- /dev/null
+++ b/demo/sql/sample_data.sql
@@ -0,0 +1,38 @@
+-- create users table
+CREATE TABLE IF NOT EXISTS users
+(
+  id integer NOT NULL,
+  login character varying(256) NOT NULL,
+  passwd character varying(256) NOT NULL,
+  is_superuser boolean NOT NULL DEFAULT false,
+  disabled boolean NOT NULL DEFAULT false,
+  CONSTRAINT user_pkey PRIMARY KEY (id),
+  CONSTRAINT user_login_key UNIQUE (login)
+);
+
+-- and permissions for them
+CREATE TABLE IF NOT EXISTS permissions
+(
+  id integer NOT NULL,
+  user_id integer NOT NULL,
+  perm_name character varying(64) NOT NULL,
+  CONSTRAINT permission_pkey PRIMARY KEY (id),
+  CONSTRAINT user_permission_fkey FOREIGN KEY (user_id)
+      REFERENCES users (id) MATCH SIMPLE
+      ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+-- insert some data
+INSERT INTO users(id, login, passwd, is_superuser, disabled)
+VALUES (1, 'admin', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', TRUE, FALSE);
+INSERT INTO users(id, login, passwd, is_superuser, disabled)
+VALUES (2, 'moderator', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', FALSE, FALSE);
+INSERT INTO users(id, login, passwd, is_superuser, disabled)
+VALUES (3, 'user', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', FALSE, FALSE);
+
+INSERT INTO permissions(id, user_id, perm_name)
+VALUES (1, 2, 'protected');
+INSERT INTO permissions(id, user_id, perm_name)
+VALUES (2, 2, 'public');
+INSERT INTO permissions(id, user_id, perm_name)
+VALUES (3, 3, 'public');
diff --git a/docs/example.rst b/docs/example.rst
index 9a29c8d..e50ba38 100644
--- a/docs/example.rst
+++ b/docs/example.rst
@@ -1,8 +1,11 @@
+.. _aiohttp-security-example:
+
+===============================================
 How to Make a Simple Server With Authorization
-==============================================
+===============================================
 
 
-.. code::python
+Simple example::
 
     import asyncio
     from aiohttp import web
@@ -13,7 +16,7 @@ How to Make a Simple Server With Authorization
         return web.Response(body=text.encode('utf-8'))
 
     # option 2: auth at a higher level?
-    # set user_id and allowed in the wsgo handler
+    # set user_id and allowed in the wsgi handler
     @protect('view_user')
     @asyncio.coroutine
     def user_handler(request):
diff --git a/docs/example_db_auth.rst b/docs/example_db_auth.rst
new file mode 100644
index 0000000..aea5a84
--- /dev/null
+++ b/docs/example_db_auth.rst
@@ -0,0 +1,210 @@
+.. _aiohttp-security-example-db-auth:
+
+===========================================
+Permissions with PostgreSQL-based storage
+===========================================
+
+Make sure that you have PostgreSQL and Redis servers up and running.
+If you want the full source code in advance or for comparison, check out
+the `demo source`_.
+
+.. _demo source:
+    https://github.com/aio-libs/aiohttp_security/tree/master/demo
+
+.. _passlib:
+    https://pythonhosted.org/passlib/
+
+Database
+--------
+
+Launch these sql scripts to init database and fill it with sample data:
+
+``psql template1 < demo/sql/init_db.sql``
+
+and then
+
+``psql template1 < demo/sql/sample_data.sql``
+
+
+You will have two tables for storing users and their permissions
+
++--------------+
+| users        |
++==============+
+| id           |
++--------------+
+| login        |
++--------------+
+| passwd       |
++--------------+
+| is_superuser |
++--------------+
+| disabled     |
++--------------+
+
+and second table is permissions table:
+
++-----------------+
+| permissions     |
++=================+
+| id              |
++-----------------+
+| user_id         |
++-----------------+
+| permission_name |
++-----------------+
+
+
+Writing policies
+----------------
+
+You need to implement two entities: *IdentityPolicy* and *AuthorizationPolicy*.
+First one should have these methods: *identify*, *remember* and *forget*.
+For second one: *authorized_userid* and *permits*. We will use built-in
+*SessionIdentityPolicy* and write our own database-based authorization policy.
+
+In our example we will lookup database by user login and if present return
+this identity::
+
+
+    @asyncio.coroutine
+    def authorized_userid(self, identity):
+        with (yield from self.dbengine) as conn:
+            where = sa.and_(db.users.c.login == identity,
+                            sa.not_(db.users.c.disabled))
+            query = db.users.count().where(where)
+            ret = yield from conn.scalar(query)
+            if ret:
+                return identity
+            else:
+                return None
+
+
+For permission check we will fetch the user first, check if he is superuser
+(all permissions are allowed), otherwise check if permission is explicitly set
+for that user::
+
+    @asyncio.coroutine
+    def permits(self, identity, permission, context=None):
+        if identity is None:
+            return False
+
+        with (yield from self.dbengine) as conn:
+            where = sa.and_(db.users.c.login == identity,
+                            sa.not_(db.users.c.disabled))
+            query = db.users.select().where(where)
+            ret = yield from conn.execute(query)
+            user = yield from ret.fetchone()
+            if user is not None:
+                user_id = user[0]
+                is_superuser = user[4]
+                if is_superuser:
+                    return True
+
+                where = db.permissions.c.user_id == user_id
+                query = db.permissions.select().where(where)
+                ret = yield from conn.execute(query)
+                result = yield from ret.fetchall()
+                if ret is not None:
+                    for record in result:
+                        if record.perm_name == permission:
+                            return True
+
+            return False
+
+
+Setup
+-----
+
+Once we have all the code in place we can install it for our application::
+
+    from aiohttp_session.redis_storage import RedisStorage
+    from aiohttp_security import setup as setup_security
+    from aiohttp_security import SessionIdentityPolicy
+    from aiopg.sa import create_engine
+    from aioredis import create_pool
+
+    from .db_auth import DBAuthorizationPolicy
+
+
+    @asyncio.coroutine
+    def init(loop):
+        redis_pool = yield from create_pool(('localhost', 6379))
+        dbengine = yield from create_engine(user='aiohttp_security',
+                                            password='aiohttp_security',
+                                            database='aiohttp_security',
+                                            host='127.0.0.1')
+        app = web.Application(loop=loop)
+        setup_session(app, RedisStorage(redis_pool))
+        setup_security(app,
+                       SessionIdentityPolicy(),
+                       DBAuthorizationPolicy(dbengine))
+        return app
+
+
+Now we have authorization and can decorate every other view with access rights
+based on permissions. This simple decorator (for class-based handlers) will
+help to do that::
+
+    def require(permission):
+        def wrapper(f):
+            @asyncio.coroutine
+            @functools.wraps(f)
+            def wrapped(self, request):
+                has_perm = yield from permits(request, permission)
+                if not has_perm:
+                    message = 'User has no permission {}'.format(permission)
+                    raise web.HTTPForbidden(body=message.encode())
+                return (yield from f(self, request))
+            return wrapped
+        return wrapper
+
+
+For each view you need to protect just apply the decorator on it::
+
+    class Web:
+        @require('protected')
+        @asyncio.coroutine
+        def protected_page(self, request):
+            response = web.Response(body=b'You are on protected page')
+            return response
+
+
+If someone will try to access this protected page he will see::
+
+    403, User has no permission "protected"
+
+
+The best part about it is that you can implement any logic you want until it
+follows the API conventions.
+
+Launch application
+------------------
+
+For working with passwords there is a good library passlib_. Once you've
+created some users you want to check their credentials on login. Similar
+function may do what you trying to accomplish::
+
+    from passlib.hash import sha256_crypt
+
+    @asyncio.coroutine
+    def check_credentials(db_engine, username, password):
+        with (yield from db_engine) as conn:
+            where = sa.and_(db.users.c.login == username,
+                            sa.not_(db.users.c.disabled))
+            query = db.users.select().where(where)
+            ret = yield from conn.execute(query)
+            user = yield from ret.fetchone()
+            if user is not None:
+                hash = user[2]
+                return sha256_crypt.verify(password, hash)
+        return False
+
+
+Final step is to launch your application::
+
+    python demo/main.py
+
+
+Try to login with admin/moderator/user accounts (with *password* password)
+and access **/public** or **/protected** endpoints.
diff --git a/docs/index.rst b/docs/index.rst
index c28aec6..7552a3c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -20,6 +20,7 @@ Contents:
    usage
    reference
    example
+   example_db_auth
    glossary
 
 
diff --git a/requirements-dev.txt b/requirements-dev.txt
index ae25724..e23faf8 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -8,3 +8,6 @@ alabaster>=0.6.2
 pep257
 aiohttp_session>=0.4.0
 aiopg[sa]
+aioredis==0.2.8
+hiredis==0.2.0
+passlib==1.6.5
diff --git a/tests/conftest.py b/tests/conftest.py
index 31f69cc..64260bd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,8 +1,10 @@
-import aiohttp
-import asyncio
 import gc
-import pytest
 import socket
+import asyncio
+
+import pytest
+import aiohttp
+
 from aiohttp import web
 
 
@@ -79,7 +81,8 @@ class Client:
         while path.startswith('/'):
             path = path[1:]
         url = self._url + path
-        return self._session.get(url, **kwargs)
+        resp = self._session.get(url, **kwargs)
+        return resp
 
     def post(self, path, **kwargs):
         while path.startswith('/'):
@@ -97,6 +100,7 @@ class Client:
 @pytest.yield_fixture
 def create_app_and_client(create_server, loop):
     client = None
+    cookie_jar = aiohttp.CookieJar(loop=loop, unsafe=True)
 
     @asyncio.coroutine
     def maker(*, server_params=None, client_params=None):
@@ -108,7 +112,11 @@ def create_app_and_client(create_server, loop):
         app, url = yield from create_server(**server_params)
         if client_params is None:
             client_params = {}
-        client = Client(aiohttp.ClientSession(loop=loop, **client_params), url)
+
+        client = Client(
+            aiohttp.ClientSession(loop=loop, cookie_jar=cookie_jar),
+            url
+        )
         return app, client
 
     yield maker
diff --git a/tests/test_cookies_identity.py b/tests/test_cookies_identity.py
index 59a691f..edb7112 100644
--- a/tests/test_cookies_identity.py
+++ b/tests/test_cookies_identity.py
@@ -34,7 +34,7 @@ def test_remember(create_app_and_client):
     app.router.add_route('GET', '/', handler)
     resp = yield from client.get('/')
     assert 200 == resp.status
-    assert 'Andrew' == client.cookies['AIOHTTP_SECURITY'].value
+    assert 'Andrew' == resp.cookies['AIOHTTP_SECURITY'].value
     yield from resp.release()
 
 
@@ -98,5 +98,6 @@ def test_forget(create_app_and_client):
     resp = yield from client.post('/logout')
     assert 200 == resp.status
     assert resp.url.endswith('/')
-    assert '' == client.cookies['AIOHTTP_SECURITY'].value
+    with pytest.raises(KeyError):
+        _ = client.cookies['AIOHTTP_SECURITY']  # noqa
     yield from resp.release()
diff --git a/tests/test_session_identity.py b/tests/test_session_identity.py
index d133ea6..81450f7 100644
--- a/tests/test_session_identity.py
+++ b/tests/test_session_identity.py
@@ -7,8 +7,8 @@ from aiohttp_security import (remember, forget,
 from aiohttp_security import setup as setup_security
 from aiohttp_security.session_identity import SessionIdentityPolicy
 from aiohttp_security.api import IDENTITY_KEY
-from aiohttp_session import (SimpleCookieStorage, session_middleware,
-                             get_session)
+from aiohttp_session import SimpleCookieStorage, get_session
+from aiohttp_session import setup as setup_session
 
 
 class Autz(AbstractAuthorizationPolicy):
@@ -27,7 +27,7 @@ def create_app_and_client2(create_app_and_client):
     @asyncio.coroutine
     def maker(*args, **kwargs):
         app, client = yield from create_app_and_client(*args, **kwargs)
-        app.middlewares.append(session_middleware(SimpleCookieStorage()))
+        setup_session(app, SimpleCookieStorage())
         setup_security(app, SessionIdentityPolicy(), Autz())
         return app, client
     return maker
@@ -82,6 +82,7 @@ def test_identify(create_app_and_client2):
     resp = yield from client.post('/')
     assert 200 == resp.status
     yield from resp.release()
+
     resp = yield from client.get('/')
     assert 200 == resp.status
     yield from resp.release()
@@ -103,7 +104,7 @@ def test_forget(create_app_and_client2):
 
     @asyncio.coroutine
     def logout(request):
-        response = web.HTTPFound(location='/')
+        response = web.HTTPFound('/')
         yield from forget(request, response)
         return response
 
@@ -111,12 +112,14 @@ def test_forget(create_app_and_client2):
     app.router.add_route('GET', '/', index)
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
+
     resp = yield from client.post('/login')
     assert 200 == resp.status
     assert resp.url.endswith('/')
     txt = yield from resp.text()
     assert 'Andrew' == txt
     yield from resp.release()
+
     resp = yield from client.post('/logout')
     assert 200 == resp.status
     assert resp.url.endswith('/')