diff --git a/terrareg/database.py b/terrareg/database.py
index 74d8381eecb48dc64e09de6b93461f27b323b7d8..32ed78c273a3d62cc4bca45fffd49bcbf33387a0 100644
--- a/terrareg/database.py
+++ b/terrareg/database.py
@@ -51,8 +51,16 @@ class Database():
         self._sub_module = None
         self._analytics = None
         self._example_file = None
+        self._session = None
         self.transaction_connection = None
 
+    @property
+    def session(self):
+        """Return session table"""
+        if self._session is None:
+            raise DatabaseMustBeIniistalisedError('Database class must be initialised.')
+        return self._session
+
     @property
     def git_provider(self):
         """Return git_provider table."""
@@ -141,6 +149,13 @@ class Database():
         LARGE_COLUMN_SIZE = 1024
         URL_COLUMN_SIZE = 1024
 
+        self._session = sqlalchemy.Table(
+            'session', meta,
+            sqlalchemy.Column('id', sqlalchemy.String(128), primary_key=True),
+            sqlalchemy.Column('expiry', sqlalchemy.Date, nullable=False),
+            sqlalchemy.Column('data', sqlalchemy.String(LARGE_COLUMN_SIZE), nullable=False)
+        )
+
         self._git_provider = sqlalchemy.Table(
             'git_provider', meta,
             sqlalchemy.Column('id', sqlalchemy.Integer, primary_key = True),
diff --git a/terrareg/models.py b/terrareg/models.py
index 07853b48fc3c4e7c2112277f559d2969d1e1ed2d..65fa242d232289b816c8c7157beaaea4ac9ed591 100644
--- a/terrareg/models.py
+++ b/terrareg/models.py
@@ -1,9 +1,11 @@
 
+import datetime
 from importlib.util import module_for_loader
 import os
 from distutils.version import LooseVersion
 import json
 import re
+import secrets
 import sqlalchemy
 import urllib.parse
 
@@ -29,6 +31,49 @@ from terrareg.utils import safe_join_paths
 from terrareg.validators import GitUrlValidator
 
 
+
+class Session:
+    """Provide interface to get and set sessions"""
+
+    SESSION_ID_LENGTH = 32
+
+    @staticmethod
+    def create_session():
+        """Create new session object."""
+        db = Database.get()
+        with db.get_connection() as conn:
+            session_id = secrets.token_urlsafe(Session.SESSION_ID_LENGTH)
+            conn.execute(db.session.insert(
+                id=session_id,
+                expiry=(datetime.datetime.now() + datetime.timedelta(minutes=terrareg.config.Config.ADMIN_SESSION_EXPIRY_MINS)),
+                data=json.dumps({})))
+
+            return session_id
+
+    @staticmethod
+    def get_session(session_id):
+        """Get session object."""
+        # Check if session exists in database and is still valid
+        return Session(session_id=session_id)
+
+    @property
+    def id(self):
+        """Return ID of session"""
+        return self._get_db_row()['id']
+
+    def __init__(self, session_id):
+        """Store current session ID."""
+        self._session_id = session_id
+
+    def get(self, attr, default):
+        """Get data attribute from session"""
+        pass
+
+    def set(self, attr, value):
+        """Set session data attribute"""
+        pass
+
+
 class GitProvider:
     """Interface to specify how modules should interact with known git providers."""
 
diff --git a/terrareg/server.py b/terrareg/server.py
index 277f7d90f2d588c2f36bf986f9b0988884767eba..c50d9bf81c3e811b068db8c9683a98d9a48373ef 100644
--- a/terrareg/server.py
+++ b/terrareg/server.py
@@ -426,7 +426,7 @@ class Server(object):
             TRUSTED_NAMESPACE_LABEL=terrareg.config.Config().TRUSTED_NAMESPACE_LABEL,
             CONTRIBUTED_NAMESPACE_LABEL=terrareg.config.Config().CONTRIBUTED_NAMESPACE_LABEL,
             VERIFIED_MODULE_LABEL=terrareg.config.Config().VERIFIED_MODULE_LABEL,
-            csrf_token=get_csrf_token()
+            csrf_token=RequestSecurity.get_csrf_token()
         )
 
     def run(self, debug=None):
@@ -586,96 +586,135 @@ class AuthenticationType(Enum):
     SESSION = 3
 
 
-def get_csrf_token():
-    """Return current session CSRF token."""
-    return session.get('csrf_token', '')
-
-
-def check_csrf_token(csrf_token):
-    """Check CSRF token."""
-    # If user is authenticated using authentication token,
-    # do not required CSRF token
-    if get_current_authentication_type() is AuthenticationType.AUTHENTICATION_TOKEN:
-        return False
-
-    session_token = get_csrf_token()
-    if not session_token:
-        raise NoSessionSetError('No session is presesnt to check CSRF token')
-    elif session_token != csrf_token:
-        raise IncorrectCSRFTokenError('CSRF token is incorrect')
-    else:
-        return True
-
-
-def get_current_authentication_type():
-    """Return the current authentication method of the user."""
-    return g.get('authentication_type', AuthenticationType.NOT_CHECKED)
-
-
-def check_admin_authentication():
-    """Check authorization header is present or authenticated session"""
-    authenticated = False
-    g.authentication_type = AuthenticationType.NOT_AUTHENTICATED
-
-    # Check that:
-    # - An admin authentication token has been setup
-    # - A token has neeif valid authorisation header has been passed
-    if (terrareg.config.Config().ADMIN_AUTHENTICATION_TOKEN and
-            request.headers.get('X-Terrareg-ApiKey', '') ==
-            terrareg.config.Config().ADMIN_AUTHENTICATION_TOKEN):
-        authenticated = True
-        g.authentication_type = AuthenticationType.AUTHENTICATION_TOKEN
-
-    # Check if authenticated via session
-    # - Ensure session key has been setup
-    if (terrareg.config.Config().SECRET_KEY and
-            session.get('is_admin_authenticated', False) and
-            'expires' in session and
-            session.get('expires').timestamp() > datetime.datetime.now().timestamp()):
-        authenticated = True
-        g.authentication_type = AuthenticationType.SESSION
-
-    return authenticated
-
-
-def require_admin_authentication(func):
-    """Check user is authenticated as admin and either call function or return 401, if not."""
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        if not check_admin_authentication():
-            abort(401)
-        else:
-            return func(*args, **kwargs)
-    return wrapper
 
+class RequestSession:
+
+    def current_session(self):
+        """Return session object based on current request"""
+        if 'session_id' in session:
+            session_id = session['session_id']
+            session = Session.get_session(session_id)
+            if session is not None:
+                return session
+
+        session = Session.create_session()
+        return None
 
-def check_api_key_authentication(api_keys):
-    """Check API key authentication."""
-    # If user is authenticated as admin, allow
-    if check_admin_authentication():
-        return True
-    # Check if no API keys have been configured
-    # and allow request
-    if not api_keys:
-        return True
+    def create_session(self):
+        """Create session for request."""
+        session = Session.create_session()
+        session['session_id'] = session.id
 
-    # Check header against list of allowed API keys
-    provided_api_key = request.headers.get('X-Terrareg-ApiKey', '')
-    return provided_api_key and provided_api_key in api_keys
 
+class RequestSecurity(RequestSession):
+    """"""
 
-def require_api_authentication(api_keys):
-    """Check user is authenticated using API key or as admin and either call function or return 401, if not."""
-    def outer_wrapper(func):
+    @classmethod
+    def require_authentication(cls, func):
+        """Wrap request methods that require authentication for request."""
         @wraps(func)
         def wrapper(*args, **kwargs):
+            request_security = cls()
+            if not request_security.check_authentication():
+                abort(401)
+            else:
+                return func(*args, **kwargs, request_security=request_security)
+        return wrapper
 
-            if not check_api_key_authentication(api_keys):
+    def check_authentication(self):
+        """Check authentication base method"""
+        raise NotImplementedError
+
+    def get_csrf_token(self):
+        """Return current session CSRF token."""
+        return self.current_session().get('csrf_token', '')
+
+    @classmethod
+    def check_csrf_token(cls, csrf_token):
+        """Check CSRF token."""
+        # If user is authenticated using authentication token,
+        # do not required CSRF token
+        if cls.get_current_authentication_type() is AuthenticationType.AUTHENTICATION_TOKEN:
+            return False
+
+        session_token = cls.get_csrf_token()
+        if not session_token:
+            raise NoSessionSetError('No session is presesnt to check CSRF token')
+        elif session_token != csrf_token:
+            raise IncorrectCSRFTokenError('CSRF token is incorrect')
+        else:
+            return True
+
+    @staticmethod
+    def get_current_authentication_type():
+        """Return the current authentication method of the user."""
+        return g.get('authentication_type', AuthenticationType.NOT_CHECKED)
+
+    @staticmethod
+    def check_admin_authentication():
+        """Check authorization header is present or authenticated session"""
+        authenticated = False
+        g.authentication_type = AuthenticationType.NOT_AUTHENTICATED
+
+        # Check that:
+        # - An admin authentication token has been setup
+        # - A token has neeif valid authorisation header has been passed
+        if (terrareg.config.Config().ADMIN_AUTHENTICATION_TOKEN and
+                request.headers.get('X-Terrareg-ApiKey', '') ==
+                terrareg.config.Config().ADMIN_AUTHENTICATION_TOKEN):
+            authenticated = True
+            g.authentication_type = AuthenticationType.AUTHENTICATION_TOKEN
+
+        # Check if authenticated via session
+        # - Ensure session key has been setup
+        if (terrareg.config.Config().SECRET_KEY and
+                session.get('is_admin_authenticated', False) and
+                'expires' in session and
+                session.get('expires').timestamp() > datetime.datetime.now().timestamp()):
+            authenticated = True
+            g.authentication_type = AuthenticationType.SESSION
+
+        return authenticated
+
+    @classmethod
+    def require_admin_authentication(cls, func):
+        """Check user is authenticated as admin and either call function or return 401, if not."""
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            if not cls.check_admin_authentication():
                 abort(401)
             else:
                 return func(*args, **kwargs)
         return wrapper
-    return outer_wrapper
+
+    @classmethod
+    def check_api_key_authentication(cls, api_keys):
+        """Check API key authentication."""
+        # If user is authenticated as admin, allow
+        if cls.check_admin_authentication():
+            return True
+        # Check if no API keys have been configured
+        # and allow request
+        if not api_keys:
+            return True
+
+        # Check header against list of allowed API keys
+        provided_api_key = request.headers.get('X-Terrareg-ApiKey', '')
+        return provided_api_key and provided_api_key in api_keys
+
+    @classmethod
+    def require_api_authentication(cls, api_keys):
+        """Check user is authenticated using API key or as admin and either call function or return 401, if not."""
+        def outer_wrapper(func):
+            @wraps(func)
+            def wrapper(*args, **kwargs):
+
+                if not cls.check_api_key_authentication(api_keys):
+                    abort(401)
+                else:
+                    return func(*args, **kwargs)
+            return wrapper
+        return outer_wrapper
 
 
 class ApiTerraformWellKnown(Resource):
@@ -855,7 +894,7 @@ class ApiModuleVersionUpload(ErrorCatchingResource):
 
     ALLOWED_EXTENSIONS = ['zip']
 
-    method_decorators = [require_api_authentication(terrareg.config.Config().UPLOAD_API_KEYS)]
+    method_decorators = [RequestSecurity.require_api_authentication(terrareg.config.Config().UPLOAD_API_KEYS)]
 
     def allowed_file(self, filename):
         """Check if file has allowed file-extension"""
@@ -902,7 +941,7 @@ class ApiModuleVersionUpload(ErrorCatchingResource):
 class ApiModuleVersionCreate(ErrorCatchingResource):
     """Provide interface to create release for git-backed modules."""
 
-    method_decorators = [require_api_authentication(terrareg.config.Config().UPLOAD_API_KEYS)]
+    method_decorators = [RequestSecurity.require_api_authentication(terrareg.config.Config().UPLOAD_API_KEYS)]
 
     def _post(self, namespace, name, provider, version):
         """Handle creation of module version."""
@@ -1610,7 +1649,7 @@ class ApiTerraregModuleSearchFilters(ErrorCatchingResource):
 class ApiTerraregIsAuthenticated(ErrorCatchingResource):
     """Interface to teturn whether user is authenticated as an admin."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _get(self):
         return {'authenticated': True}
@@ -1619,7 +1658,7 @@ class ApiTerraregIsAuthenticated(ErrorCatchingResource):
 class ApiTerraregAdminAuthenticate(ErrorCatchingResource):
     """Interface to perform authentication as an admin and set appropriate cookie."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _post(self):
         """Handle POST requests to the authentication endpoint."""
@@ -1640,7 +1679,7 @@ class ApiTerraregAdminAuthenticate(ErrorCatchingResource):
 class ApiTerraregModuleProviderCreate(ErrorCatchingResource):
     """Provide interface to create module provider."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _post(self, namespace, name, provider):
         """Handle update to settings."""
@@ -1690,7 +1729,7 @@ class ApiTerraregModuleProviderCreate(ErrorCatchingResource):
 
         args = parser.parse_args()
 
-        check_csrf_token(args.csrf_token)
+        RequestSecurity.check_csrf_token(args.csrf_token)
 
         # Update repository URL of module version
         namespace = Namespace(name=namespace)
@@ -1768,7 +1807,7 @@ class ApiTerraregModuleProviderCreate(ErrorCatchingResource):
 class ApiTerraregModuleProviderDelete(ErrorCatchingResource):
     """Provide interface to delete module provider."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _delete(self, namespace, name, provider):
         """Delete module provider."""
@@ -1783,7 +1822,7 @@ class ApiTerraregModuleProviderDelete(ErrorCatchingResource):
 
         args = parser.parse_args()
 
-        check_csrf_token(args.csrf_token)
+        RequestSecurity.check_csrf_token(args.csrf_token)
 
         # Update repository URL of module version
         namespace = Namespace(name=namespace)
@@ -1799,7 +1838,7 @@ class ApiTerraregModuleProviderDelete(ErrorCatchingResource):
 class ApiTerraregModuleVersionDelete(ErrorCatchingResource):
     """Provide interface to delete module version."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _delete(self, namespace, name, provider, version):
         """Delete module version."""
@@ -1814,7 +1853,7 @@ class ApiTerraregModuleVersionDelete(ErrorCatchingResource):
 
         args = parser.parse_args()
 
-        check_csrf_token(args.csrf_token)
+        RequestSecurity.check_csrf_token(args.csrf_token)
 
         # Update repository URL of module version
         namespace_obj = Namespace(name=namespace)
@@ -1840,7 +1879,7 @@ class ApiTerraregModuleVersionDelete(ErrorCatchingResource):
 class ApiTerraregModuleProviderSettings(ErrorCatchingResource):
     """Provide interface to update module provider settings."""
 
-    method_decorators = [require_admin_authentication]
+    method_decorators = [RequestSecurity.require_admin_authentication]
 
     def _post(self, namespace, name, provider):
         """Handle update to settings."""
@@ -1897,7 +1936,7 @@ class ApiTerraregModuleProviderSettings(ErrorCatchingResource):
 
         args = parser.parse_args()
 
-        check_csrf_token(args.csrf_token)
+        RequestSecurity.check_csrf_token(args.csrf_token)
 
         # Update repository URL of module version
         namespace = Namespace(name=namespace)
@@ -1972,7 +2011,7 @@ class ApiTerraregModuleProviderSettings(ErrorCatchingResource):
 class ApiTerraregModuleVersionPublish(ErrorCatchingResource):
     """Provide interface to publish module version."""
 
-    method_decorators = [require_api_authentication(terrareg.config.Config().PUBLISH_API_KEYS)]
+    method_decorators = [RequestSecurity.require_api_authentication(terrareg.config.Config().PUBLISH_API_KEYS)]
 
     def _post(self, namespace, name, provider, version):
         """Publish module."""