diff --git a/Readme.md b/Readme.md
index 00857c9fac3990a5a1357421f7a7c411278c03c5..4a569d41e8b9390355672baf03c3573487aec1bd 100644
--- a/Readme.md
+++ b/Readme.md
@@ -12,3 +12,17 @@ Issues and further documentation can be found there.
 ## Development
 
 A complete list of the `Minion.data` json structure can be found [here](https://lab.it.hs-hannover.de/django/salt-observer/wikis/minion-data-structure) in the wiki.
+
+
+```python
+# get output of 'w' command of all 'tgt' minions
+request('token', {'tgt': '*', 'fun': 'cmd.run', 'arg': 'w'})
+
+# get all grains of 'tgt' minions
+request('token', {'tgt': '*', 'fun': 'grains.items'})
+
+# execute some state and apply custom pillars to it
+request('token', {'tgt': '*', 'fun': 'state.sls', 'kwarg': {
+    'mods': 'name_of_state', 'pillar': {'some': 'pillar_data'}
+}})
+```
diff --git a/salt_observer/backends.py b/salt_observer/backends.py
index ab4dcee457959206c95a8e402ba39c0cacb2ecbb..8e60c604266fa8b98af3036763037e4825488ba2 100644
--- a/salt_observer/backends.py
+++ b/salt_observer/backends.py
@@ -1,24 +1,29 @@
 from django.contrib.auth.backends import ModelBackend
 from django.contrib.auth.models import User
 
-from salt_observer.cherry import SaltCherrypyApi
+from salt_observer.saltapis import SaltCherrypy, SaltTornado
 
 
 class RestBackend(ModelBackend):
     ''' Authenticate against salt-api-permissions '''
 
-    def authenticate(self, username=None, password=None):
+    def authenticate(self, username=None, password=None, request=None):
 
         try:
-            valid = SaltCherrypyApi.obtain_auth_token(username, password)
-        except:
-            valid = False
+            cherrypy_token = SaltCherrypy(username, password).token
+            tornado_token = SaltTornado(username, password).token
+        except Exception as e:
+            cherrypy_token = False
+            tornado_token = False
 
-        if valid:
+        if cherrypy_token and tornado_token:
             try:
                 user = User.objects.get(username=username)
             except User.DoesNotExist:
                 user = User.objects.create_user(username=username, email='', password=password)
 
+            request.session['salt_cherrypy_token'] = cherrypy_token
+            request.session['salt_tornado_token'] = tornado_token
+
             return user
         return None
diff --git a/salt_observer/cherry.py b/salt_observer/cherry.py
deleted file mode 100644
index 0c6e7b79707236f3b08fb983d99df50f75fb3fd8..0000000000000000000000000000000000000000
--- a/salt_observer/cherry.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from django.conf import settings
-
-import requests
-from getpass import getpass
-
-
-class SaltCherrypyApi(object):
-
-    BASE_URL = '{protocol}://{host}:{port}'.format(**settings.SALT_API)
-
-    def __init__(self, username, password):
-        ''' Log in every time an instance is created '''
-        self.token = self.obtain_auth_token(username, password)
-
-    @classmethod
-    def obtain_auth_token(cls, username, password):
-        res = requests.post(cls.BASE_URL+'/login', headers={'Accept': 'application/json'}, data={
-            'username': username,
-            'password': password,
-            'eauth': 'pam'
-        })
-
-        if res.status_code != 200:
-            raise Exception('{} - {}'.format(res.status_code, res.text))
-
-        return res.json().get('return')[0].get('token')
-
-    def request(self, data, api_point=''):
-        data.update({'client': 'local'})
-        return requests.post(
-            '{}/{}'.format(self.BASE_URL, api_point),
-            headers={
-                'Accept': 'application/json',
-                'X-Auth-Token': self.token
-            },
-            data=data
-        )
-
-    def logout(self):
-        return requests.post(
-            self.BASE_URL+'/logout',
-            headers={
-                'Accept': 'application/json',
-                'X-Auth-Token': self.token
-            }
-        )
-
-    def get(self, module, target='*', api_args=[], api_kwargs={}):
-        return self.request({
-            'fun': module,
-            'tgt': target,
-            'arg': api_args,
-            'kwarg': api_kwargs
-        }).json().get('return')[0]
-
-
-# experimental:
-# ---
-#
-# get output of 'w' command of all 'tgt' minions
-# request('token', {'tgt': '*', 'fun': 'cmd.run', 'arg': 'w'})
-#
-# get all grains of 'tgt' minions
-# request('token', {'tgt': '*', 'fun': 'grains.items'})
-#
-# execute some state and apply custom pillars to it
-# request('token', {'tgt': '*', 'fun': 'state.sls', 'kwarg': {
-#     'mods': 'name_of_state', 'pillar': {'some': 'pillar_data'}
-# }})
diff --git a/salt_observer/forms.py b/salt_observer/forms.py
index 0a467b170810b75188d89a855b33a439bbcc694e..73707e1904907ead7132b0438ffb0c5d37b0f781 100644
--- a/salt_observer/forms.py
+++ b/salt_observer/forms.py
@@ -1,4 +1,5 @@
 from django import forms
+from django.contrib.auth import authenticate
 from django.contrib.auth.forms import AuthenticationForm
 from django.utils.translation import ugettext, ugettext_lazy as _
 
@@ -74,6 +75,31 @@ class LoginForm(AuthenticationForm):
     username = forms.CharField(label='', max_length=255)
     password = forms.CharField(label='', widget=forms.PasswordInput)
 
+    def clean(self):
+        '''
+            Overwriting this method to pass the current request down to the
+            backends via authenticate([...])
+        '''
+        username = self.cleaned_data.get('username')
+        password = self.cleaned_data.get('password')
+
+        if username and password:
+            self.user_cache = authenticate(
+                username=username,
+                password=password,
+                request=self.request
+            )
+            if self.user_cache is None:
+                raise forms.ValidationError(
+                    self.error_messages['invalid_login'],
+                    code='invalid_login',
+                    params={'username': self.username_field.verbose_name},
+                )
+            else:
+                self.confirm_login_allowed(self.user_cache)
+
+        return self.cleaned_data
+
 
 class MinionEditForm(MarkdownFormMixin):
     class Meta:
diff --git a/salt_observer/management/commands/__init__.py b/salt_observer/management/commands/__init__.py
index c83ca2d11d058b1da850a02f704b9b51b67ca48e..7e5d299b2816f4876b830886f55ee0d1a93fa16e 100644
--- a/salt_observer/management/commands/__init__.py
+++ b/salt_observer/management/commands/__init__.py
@@ -1,4 +1,4 @@
-from salt_observer.cherry import SaltCherrypyApi
+from salt_observer.saltapis import SaltCherrypy
 
 from getpass import getpass
 
@@ -20,4 +20,4 @@ class ApiCommand(object):
         else:
             password = kwargs.get('password')
 
-        return SaltCherrypyApi(username, password)
+        return SaltCherrypy(username, password)
diff --git a/salt_observer/private_settings.example.py b/salt_observer/private_settings.example.py
index 2a4ab5d4af8bb7a2ae58118c382b025e337f9f0c..b1313ed93ecf438ed58b7f354bdb2e83b4206e46 100644
--- a/salt_observer/private_settings.example.py
+++ b/salt_observer/private_settings.example.py
@@ -26,9 +26,16 @@ DATABASES = {
 
 # Salt observer configuration
 SALT_API = {
-    'host': 'localhost',
-    'port': 8989,
-    'protocol': 'http',
+    'cherrypy': {
+        'host': 'localhost',
+        'port': 8989,
+        'protocol': 'http',
+    },
+    'tornado': {
+        'host': 'localhost',
+        'port': 8002,
+        'protocol': 'http'
+    }
 }
 
 # specify your salt network here to disable it in network visualization
diff --git a/salt_observer/saltapis.py b/salt_observer/saltapis.py
new file mode 100644
index 0000000000000000000000000000000000000000..befe3238280c2907330d2bb956544a5bbd183d6f
--- /dev/null
+++ b/salt_observer/saltapis.py
@@ -0,0 +1,74 @@
+from django.conf import settings
+
+import requests
+from getpass import getpass
+
+
+class AbstractApi(object):
+    '''
+        Defines an abstract api to inherit from.
+        You MUST specify a BASE_URL for your api to build a proper request url.
+    '''
+
+    BASE_URL = ''
+
+    def __init__(self, username='', password='', token=''):
+        ''' Set a token to work with '''
+        if not self.BASE_URL:
+            raise NotImplementedError('Please provide an BASE_URL')
+
+        if token:
+            self.token = token
+        else:
+            self.token = self.obtain_auth_token(username, password)
+
+    def request(self, method='get', resource='/', headers={}, data={}):
+        return getattr(requests, method)(
+            self.BASE_URL + resource,
+            headers=headers,
+            data=data
+        )
+
+    def obtain_auth_token(self, username, password):
+        res = self.request('post', '/login', headers={'Accept': 'application/json'}, data={
+            'username': username,
+            'password': password,
+            'eauth': 'pam'
+        })
+
+        if res.status_code != 200:
+            raise Exception('{} - {}'.format(res.status_code, res.text))
+
+        return res.json().get('return')[0].get('token')
+
+
+class SaltCherrypy(AbstractApi):
+
+    BASE_URL = '{protocol}://{host}:{port}'.format(**settings.SALT_API['cherrypy'])
+
+    def logout(self):
+        return self.request('post', '/logout', headers={
+            'Accept': 'application/json',
+            'X-Auth-Token': self.token
+        })
+
+    def get(self, module, target='*', api_args=[], api_kwargs={}):
+        return self.request(
+            'post',
+            data={
+                'client': 'local',
+                'fun': module,
+                'tgt': target,
+                'arg': api_args,
+                'kwarg': api_kwargs
+            },
+            headers={
+                'Accept': 'application/json',
+                'X-Auth-Token': self.token
+            }
+        ).json().get('return')[0]
+
+
+class SaltTornado(AbstractApi):
+
+    BASE_URL = '{protocol}://{host}:{port}'.format(**settings.SALT_API['tornado'])
diff --git a/salt_observer/views.py b/salt_observer/views.py
index cfee1b25189d5de2d48b222994d511028e769622..9be9b9a339dc46db2aa17526c21707f6e6a89210 100644
--- a/salt_observer/views.py
+++ b/salt_observer/views.py
@@ -49,6 +49,12 @@ class Login(FormView):
         login(self.request, form.get_user())
         return super().form_valid(form, *args, **kwargs)
 
+    def get_form_kwargs(self, *args, **kwargs):
+        '''dirty hack to pass the current request down to the backends'''
+        kwargs = super().get_form_kwargs(*args, **kwargs)
+        kwargs['request'] = self.request
+        return kwargs
+
     def get_success_url(self):
         return self.request.GET.get('next', reverse_lazy('dashboard'))