diff --git a/README.md b/README.md
index 63f737981a94cd6a331b2727f9de34195c770912..9887b68ea37d69aed3c50f0438c27b08634e010c 100644
--- a/README.md
+++ b/README.md
@@ -2,93 +2,119 @@
 
 #### Minimal Intro:
 - [SSO](https://lmddgtfy.net/?q=SSO): Single Sign On
-- SP: Service Provider (your client)
-- IdP: Identity Provider (server, Shibboleth)
-- Metadata/Meta: an XML that describes SP or IdP (or another entity)
-- SAML/SAML2: It's just another XML-based enterprise-grade standard that will make you cry blood
+- SLO: Single Log Out
+- SP: Service Provider (your web app)
+- IdP: Identity Provider (server with some dreadful software like Shibboleth)
+- Metadata/Meta: an XML that describes SP or IdP (or some other entity)
+- SAML/SAML2: It's just another XML-based enterprise-grade standard that will make you cry blood and shit bricks
 
 
-#### Necessary stuff
+#### Necessary Stuff
 - Binary dependencies: `sudo apt install libxml2-dev libxslt1-dev xmlsec1 libxmlsec1-dev pkg-config`
 - Python dependencies: see `requirements.txt` or `setup.py`
 - Add the app into `INSTALLED_APPS`
 - Include the app's `urls.py` into the project `urls.py` `urlpatterns`, preferably without a prefix
 
 
-#### Development setup
+#### Development Setup
 
-- If you don't need a fully functional SSO, you don't need to configure anything at all. Use the dev view: `localhost:8000/dev/` (might change depending on your urlconf and settings).
-
-- If you need a fully functional SSO during development, here's an example:
+- Simply use `localhost:8000/dev/` _(might change depending on your urlconf)_ for all the logging-in-out-users-groups stuff
+- To make your life easier, add the following to your project settings: 
+```python
+LOGIN_URL = urls.reverse_lazy("sso-dev")
+```
+- If you want to debug `ssoauth` or need a fully functional SSO during development for some other reason, an example is below. For additional info reference production setup and `ssoauth/app_settings/defaults.py`. If you also want a working SLO during development you will need SSL for your localhost, `nginx` will be your best friend.
 
 ```python
 """ settings/dev.py """
-
+import os, socket
 from django import urls
-import socket
-import os
 
 IDP_META_URL = "https://idp-test.it.hs-hannover.de/idp/shibboleth"
+IDP_LOGOUT_URL = "https://idp-test.it.hs-hannover.de/idp/profile/Logout"
 
 SP_KEY = "{project_settings}/cert/sp.key"
 SP_CERT = "{project_settings}/cert/sp.pem"
-SP_FORCE_ENTITY_ID = "dev-auto-id-{0}-{1}".format(socket.gethostname(), os.path.dirname(os.path.dirname(__file__)))
 
 SP_HOST = "localhost"
 SP_PORT = 8000
 SP_SSL = False
 
+SP_FORCE_ENTITY_ID = "dev-id-{0}-{1}".format(socket.gethostname(), os.path.dirname(os.path.dirname(__file__)))  # too many localhosts around
+
 LOGIN_URL = urls.reverse_lazy("sso-dev")
 ```
 
-#### Groups
-To receive groups over SSO you need a mapping. You can manage group mapping with `group_mapping` management command. Example:
+
+#### Regarding Logging Out
+
+After logging out locally, user will be redirected to one of the following (with this priority):
+  - `?next=` GET value (Note: instead of `next` use use `django.contrib.auth.REDIRECT_FIELD_NAME`)  
+  - `LOGOUT_REDIRECT_URL` (vanilla django setting), recommended values are:
+    - `LOGOUT_REDIRECT_URL = None` for the default behavior
+    - `LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logout-idp")` if you want to instantly initiate IDP log out, possibly triggering SLO
+    - `LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logged-out-locally")`
+  - Default: redirect to the view that asks users what to do next. Don't forget to override template `ssoauth/logged_out_locally.html`
+
+**SLO**: SLO/SLS is disabled by default. If you want to try your luck with you should add to your project settings `SP_SLS_ENABLED = True`
+
+Currently only IdP-initiated SLO is supported by this app. The only supported binding type is HTTP-Redirect due to the limitations of the underlying library used.
+
+For SLO with HTTP-Redirect to work, the SLS page must be included as `<iframe>`. Your server and/or browser might restrict such behavior. Start with setting `SP_SLS_X_FRAME_OPTIONS` (see the `ssoauth` default settings file). 
+If you have `nginx` serving pages to users, you might need to configure `x-frame-options` for the SLS view (Only the SLS view, nowhere else!). Additionally you might need to configure CSP on the web server on the IdP side. Anyways it will most likely be a lot of [fun](https://duckduckgo.com/?q=dwarf+fortress+fun) for you.
+
+
+#### Groups and Permissions
+To receive groups over SSO you need a group mapping (and of course a properly configured IdP). You can manage group mapping with `group_mapping` management command. Example:
     
     group_mapping add myproject_superusers "CN=MyProjectSuperusers,OU=Foo,OU=Bar,DC=fh-h,DC=de"
 
-*Groups are not mapped automatically. Because automatic mapping can pose security risks. Imagine auto-mapping that expects group with name "Superusers"; an intruder could create a new group with this name under any path they own and/or create an alias/reference and receive superuser permissions in your project.* 
+*Groups are not mapped automatically. The reason is that automatic mapping can pose security risks. Imagine auto-mapping that expects group with name "Superusers"; an intruder could create new group with this name under any path they own and/or create an alias/reference and receive superuser permissions in your project.* 
 
 
-#### Production setup
+#### Production Settings
+_(Disclaimer: this example might be incomplete. Reference the `ssoauth` default settings file.)_
 
 ```python
 """ settings/prod.py """
 from django import urls
 
-SP_HOST = "141.71.foo.bar"  # your SP host or IP address
+SP_HOST = "foobar.it.hs-hannover.de"  # FQDN of your SP
 IDP_META_URL = "https://idp.hs-hannover.de/idp/shibboleth"  # production
+IDP_LOGOUT_URL = "https://idp.it.hs-hannover.de/idp/profile/Logout"  # web page for IdP logout (might initiate SLO)
 
-LOGIN_URL = urls.reverse_lazy("sso-login")
+LOGIN_URL = urls.reverse_lazy("sso-login")  # django setting
 ```
-You will also need to configure the SSO
-
-#### SSO configuration
-
-
-- create a key pair:
-  - if you don't need your cert to be signed you can use `openssl req -new -x509 -days 3650 -nodes -out sp.pem -keyout sp.key`
-  - create `cert` directory:
-    - inside of project `settings` directory if it's a package
-    - next to project `settings.py` file if it's a module
-  - `./cert/sp.key` put the private key here
-  - `./cert/sp.pem` put the certificate here, signing is optional
-- configure the IdP:
-  - Ask somebody who knows more. Seriously.
-  - Try on the test IdP before you brick the production!
-  - Grab your meta
-    - Run your project.
-    - Find meta of your SP (relative path `/saml2/meta` or view name `sso-saml2-meta`)
-    - Use Ctrl+U ("view source") to get the actual XML, otherwise your browser could mess it up
-  - configure the IdP:
-    - `SSH` to the IdP and locate the Shibboleth directory, most likely `/opt/shibboleth-idp/`
-    - put your meta into a new file in `./metadata/` and give it a nice verbose name
-    - edit `./conf/metadata-providers.xml`
-      - create a new `<MetadataProvider .../>` element, your best guess is to copy an existing line that belongs to some existing Django project
-      - `id` should be unique
-      - `metadataFile` should point on your new metadata
-     - edit `./conf/attribute-filter.xml`
-       - add a new `<Rule .../>` element inside of `<AttributeFilterPolicy id="releaseToDjango">`
-       - `value` (looks like URI) should be the `entityID` of your SP (you find it in your meta)
-    - `systemctl restart tomcat8` (you now have some time to grab you a coffee)
-    - ensure the IdP works after restarting!
+
+#### Certs
+
+- if you don't need your cert to be signed you can use `openssl req -new -x509 -days 3650 -nodes -out sp.pem -keyout sp.key`
+- create `cert` directory:
+  - inside of project `settings` directory if it's a package
+  - next to project `settings.py` file if it's a module
+- `./cert/sp.key` put the private key here
+- `./cert/sp.pem` put the certificate here, signing is optional
+- if you prefer your keys somewhere else, set `SP_KEY` and `SP_CERT` settings
+
+
+#### Add an SP to an IdP
+
+- Ask somebody who knows more. Seriously.
+- Try on the test IdP before you brick the production!
+- Grab your meta
+  - Run your project.
+  - Find meta of your SP (relative path `/saml2/meta`)
+  - Use Ctrl+U ("view source") to get the actual XML, otherwise your browser could mess it up
+- IdP:
+  - `SSH` to the IdP and locate the Shibboleth directory, most likely `/opt/shibboleth-idp/`
+  - put your meta into a new file in `./metadata/` and give it a nice verbose name
+  - edit `./conf/metadata-providers.xml`
+    - create a new `<MetadataProvider .../>` element, your best guess is to copy an existing line that belongs to some existing Django project
+    - `id` should be unique
+    - `metadataFile` should point on your new metadata
+   - edit `./conf/attribute-filter.xml`
+     - add a new `<Rule .../>` element inside of `<AttributeFilterPolicy id="releaseToDjango">`
+     - `value` (looks like URI) should be the `entityID` of your SP (you find it in your meta)
+  - `systemctl restart tomcat8` (you now have some time to grab you a coffee)
+  - ensure the IdP works after restarting!
 
diff --git a/ssoauth/app_settings/__init__.py b/ssoauth/app_settings/__init__.py
index b043f659d4f779d78d617a99463d60c8818f7793..a37efc20322ec52082dfc2fbed1fd2547945e663 100644
--- a/ssoauth/app_settings/__init__.py
+++ b/ssoauth/app_settings/__init__.py
@@ -16,7 +16,6 @@ for setting_name in [k for k in globals().keys() if k.isupper()]:
                 pass  # not set
 
 
-
 # template for OneLogin toolkit settings
 
 _SET_ON_RUNTIME = None and "will be set on runtime"
@@ -51,6 +50,7 @@ ONELOGIN_SETTINGS_TEMPLATE = {
             "binding": onelogin_settings.OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT,
         },
         "singleLogoutService": {
+            # note: as for now, SP-initiated SLO is not supported by this app
             "url": _SET_ON_RUNTIME or str(),
             "binding": onelogin_settings.OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT,
         }
diff --git a/ssoauth/app_settings/defaults.py b/ssoauth/app_settings/defaults.py
index 5207242d75c1645941e26cc07d6f0436607ec981..8cd0a3d268fe05bdbef8d61f1ea5472b2f5c5f7b 100644
--- a/ssoauth/app_settings/defaults.py
+++ b/ssoauth/app_settings/defaults.py
@@ -1,4 +1,5 @@
 import os
+from django import urls
 
 
 """
@@ -18,6 +19,7 @@ SP_PORT = 443
 SP_SSL = True
 
 IDP_META_URL = None  # e.g. "https://idp-test.hs-hannover.de/idp/shibboleth"
+IDP_LOGOUT_URL = None  # e.g. "https://idp-test.it.hs-hannover.de/idp/profile/Logout"
 
 SP_KEY = "{project_settings}/cert/sp.key"
 SP_CERT = "{project_settings}/cert/sp.pem"
@@ -26,6 +28,8 @@ SSO_REQUIRED_IN_DEBUG = False
 SSO_REQUIRED_IN_PRODUCTION = False  # disabled because of e.g. collectstatic on the static server
 SSO_REQUIRED_OUTSIDE_MANAGE_PY = True  # enabled to ensure that production (that uses WSGI) has SSO
 
+SP_SLS_ENABLED = False  # single log out creates too many problems, so it is disabled for now
+SP_SLS_X_FRAME_OPTIONS = None  # in case you encounter problems with SLS view not allowed inside of an iframe, e.g. "ALLOW-FROM idp-test.it.hs-hannover.de idp.hs-hannover.de"
 
 """
 Settings you might want to change on development (don't change them for production):
diff --git a/ssoauth/checks.py b/ssoauth/checks.py
index d89ecf5feadd3ca7fc0a814212da5081e1b23226..02844199a7ad79870ca52a55ee9e6f89dd31ea35 100644
--- a/ssoauth/checks.py
+++ b/ssoauth/checks.py
@@ -78,11 +78,22 @@ def old_settings(app_configs, **kwargs):
     return errors
 
 
+@register(Tags.compatibility)
+def sp_host_is_not_localhost(app_configs, **kwargs):
+    errors = list()
+    if app_settings.SP_HOST:
+        if app_settings.SP_HOST.lower() in ("localhost", "127.0.0.1", "::1",):
+            if app_settings.SSO_REQUIRED:
+                errors.append(Error("SP_HOST should not be set to localhost: it breaks SLO, causes entityID collisions and all sorts of nasty stuff."))
+            else:
+                if not app_settings.SP_FORCE_ENTITY_ID:
+                    errors.append(Error("If your SP_HOST is localhost, take care to set a unique SP_FORCE_ENTITY_ID"))
+    return errors
+
+
 @register(Tags.urls)
 def auth_urls_configured(app_configs, **kwargs):
     errors = list()
-    if not app_settings.SSO_REQUIRED:
-        return errors
 
     def _url_from_setting(setting_name):
         # get url based on django setting name, handles most of cases of something going wrong
@@ -94,19 +105,22 @@ def auth_urls_configured(app_configs, **kwargs):
             errors.append(Warning("Failed to resolve an URL for {s}. {ec}: {e}".format(ec=e.__class__.__name__, e=str(e),s=setting_name), obj=conf.settings))
             return None
 
-    # login
-    login_url_needed = urls.reverse("sso-login")
-    login_url_current = _url_from_setting("LOGIN_URL")
-    if login_url_current != login_url_needed:
-        errors.append(Warning(
-            "LOGIN_URL must point on {login_url_needed}, not on {login_url_current}. Add the following to your project settings: "
-            "LOGIN_URL = urls.reverse_lazy(\"sso-login\")".format(**locals()),
-            obj=conf.settings,
-        ))
-    # default landing and logout pages
-    for setting_name in ("LOGIN_REDIRECT_URL", "LOGOUT_REDIRECT_URL",):
-        if not _url_from_setting(setting_name):
-            errors.append(Warning("{setting_name} is not found or invalid.".format(**locals()), obj=conf.settings,))
+    # # default landing and logout pages
+    # # probably this part is not longer required because how the logout process works
+    # for setting_name in ("LOGIN_REDIRECT_URL", "LOGOUT_REDIRECT_URL",):
+    #     if not _url_from_setting(setting_name):
+    #         errors.append(Warning("{setting_name} is not found or invalid.".format(**locals()), obj=conf.settings,))
+
+    if app_settings.SSO_REQUIRED:
+        # login
+        login_url_needed = urls.reverse("sso-login")
+        login_url_current = _url_from_setting("LOGIN_URL")
+        if login_url_current != login_url_needed:
+            errors.append(Warning(
+                "LOGIN_URL must point on {login_url_needed}, not on {login_url_current}. Add the following to your project settings: "
+                "LOGIN_URL = urls.reverse_lazy(\"sso-login\")".format(**locals()),
+                obj=conf.settings,
+            ))
     return errors
 
 
diff --git a/ssoauth/sso_utils.py b/ssoauth/sso_utils.py
index d8786b7861a2441ff1197e6844b4bb4e2c2d4cf9..b07fb8c828657d1d5918b27edc2467f6cc292c05 100644
--- a/ssoauth/sso_utils.py
+++ b/ssoauth/sso_utils.py
@@ -76,25 +76,28 @@ def get_idp_runtime_info(meta_url):
 def create_onelogin_settings(template=app_settings.ONELOGIN_SETTINGS_TEMPLATE):
     """ This function is intended to be run only once, on app startup. Raises exceptions. """
     # get the template
-    template = copy(template)
+    settings = copy(template)
     # prepare some values
-    protocol = "https" if app_settings.SP_SSL else "http"
-    port_suffix = "" if app_settings.SP_PORT in [80, 443] else ":{0}".format(app_settings.SP_PORT)
-    host = app_settings.SP_HOST
-    host_full = "{protocol}://{host}{port_suffix}".format(**locals())
+    assert app_settings.SP_HOST, "SP_HOST is not set."  # before OneLogin toolkit chokes with "sp_acs_url_invalid,sp_sls_url_invalid"
+    host_full = "{protocol}://{host}{port_suffix}".format(
+        host=app_settings.SP_HOST,
+        protocol="https" if app_settings.SP_SSL else "http",
+        port_suffix="" if app_settings.SP_PORT in [80, 443] else ":{0}".format(app_settings.SP_PORT)
+    )
     # IDP settings
-    assert app_settings.IDP_META_URL, "IDP_META_URL is not specified"  # before get_idp_runtime_info starts logging errors
+    assert app_settings.IDP_META_URL, "IDP_META_URL is not set"  # before get_idp_runtime_info starts logging errors
     idp_info = get_idp_runtime_info(app_settings.IDP_META_URL)
-    template["idp"]["x509certMulti"]["signing"] = idp_info["certificates"]["signing"]
-    template["idp"]["x509certMulti"]["encryption"] = idp_info["certificates"]["encryption"]
-    template["idp"]["singleSignOnService"]["url"] = idp_info["bindings"]["sso_redirect"]
-    template["idp"]["singleLogoutService"]["url"] = idp_info["bindings"]["slo_redirect"]
+    settings["idp"]["x509certMulti"]["signing"] = idp_info["certificates"]["signing"]
+    settings["idp"]["x509certMulti"]["encryption"] = idp_info["certificates"]["encryption"]
+    settings["idp"]["singleSignOnService"]["url"] = idp_info["bindings"]["sso_redirect"]
+    settings["idp"]["singleLogoutService"]["url"] = idp_info["bindings"]["slo_redirect"]
     # SP settings
-    template["sp"]["entityId"] = app_settings.SP_FORCE_ENTITY_ID or (host_full + urls.reverse("sso-saml2-meta"))
-    template["sp"]["assertionConsumerService"]["url"] = host_full + urls.reverse("sso-saml2-acs")
-    template["sp"]["singleLogoutService"]["url"] = host_full + urls.reverse("sso-saml2-sls")
-    template["sp"]["x509cert"] = read_key(app_settings.SP_CERT)
-    template["sp"]["privateKey"] = read_key(app_settings.SP_KEY)
+    settings["sp"]["entityId"] = app_settings.SP_FORCE_ENTITY_ID or (host_full + urls.reverse("sso-saml2-meta"))
+    settings["sp"]["assertionConsumerService"]["url"] = host_full + urls.reverse("sso-saml2-acs")
+    if app_settings.SP_SLS_ENABLED:
+        settings["sp"]["singleLogoutService"]["url"] = host_full + urls.reverse("sso-saml2-sls")
+    settings["sp"]["x509cert"] = read_key(app_settings.SP_CERT)
+    settings["sp"]["privateKey"] = read_key(app_settings.SP_KEY)
     # done
-    return OneLogin_Saml2_Settings(settings=template, sp_validation_only=True)
+    return OneLogin_Saml2_Settings(settings=settings, sp_validation_only=True)
 
diff --git a/ssoauth/templates/ssoauth/dev.html b/ssoauth/templates/ssoauth/dev.html
index cddbd57d82b7999382060e54963d9cf10f4c1c66..3d3ddb80fa17d11b87343d8e258d210c0e6f706b 100644
--- a/ssoauth/templates/ssoauth/dev.html
+++ b/ssoauth/templates/ssoauth/dev.html
@@ -45,7 +45,7 @@
                     <div class="column {% if not sso_configured %}disabled{% endif %}">
                         <h4 class="title">Production Actions</h4>
                         <a class="button button-outline button-narrow" href="{% url "sso-login" %}?next={% url "sso-dev" %}">Log In</a>
-                        <a class="button button-outline button-narrow" href="{% url "sso-logout" %}?next={% url "sso-dev" %}">Log Out</a>
+                        <a class="button button-outline button-narrow" href="{% url "sso-logout" %}">Log Out</a>
                         <a class="button button-outline button-narrow" href="{% url "sso-saml2-meta" %}" target="_blank">Meta</a>
                         <p><i>{% if sso_configured %}These actions are used in production{% else %}Your SSO settings are a potato{% endif %}</i></p>
                     </div>
diff --git a/ssoauth/templates/ssoauth/logged_out_locally.html b/ssoauth/templates/ssoauth/logged_out_locally.html
new file mode 100644
index 0000000000000000000000000000000000000000..10cedbcf542b3cc8e75ca85a73e7b940b0771855
--- /dev/null
+++ b/ssoauth/templates/ssoauth/logged_out_locally.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Logged Out</title>
+</head>
+<body>
+    <h2>Logged Out</h2>
+    <hr/>
+    <p>You have successfully logged out of <b><code>{{ THIS_SITE }}</code></b>.</p>
+    <hr/>
+    <p>
+        However, you will still be able to automatically log in to most of our services using Single Sign On.
+    </p>
+    <ul>
+        <li><a href="{{ IDP_LOGOUT_URL }}">Click here</a> if you would like to log out of the Single Sign On service.</li>
+        <li>When you close your browser you will be logged out of all our services that use Single Sign On.</li>
+    </ul>
+</body>
+</html>
diff --git a/ssoauth/urls.py b/ssoauth/urls.py
index 9048579403cd3bebca9135b4b7b8fca6d4826a27..a8777ed1260388543de6bb35d6d445dda124fe16 100644
--- a/ssoauth/urls.py
+++ b/ssoauth/urls.py
@@ -4,6 +4,8 @@ from . import views
 urlpatterns = (
     url(r"^login/?$", views.LogInView.as_view(), name="sso-login"),
     url(r"^logout/?$", views.LogOutView.as_view(), name="sso-logout"),
+    url(r"^logout/message/?$", views.LoggedOutLocallyView.as_view(), name="sso-logged-out-locally"),
+    url(r"^logout/idp/?$", views.IdpLogoutRedirectView.as_view(), name="sso-logout-idp"),
     url(r"^saml2/acs/?$", views.ACSAuthNView.as_view(), name="sso-saml2-acs"),
     url(r"^saml2/sls/?$", views.SLSView.as_view(), name="sso-saml2-sls"),
     url(r"^saml2/meta(?:data)?/?$", views.MetadataView.as_view(), name="sso-saml2-meta"),
diff --git a/ssoauth/views.py b/ssoauth/views.py
index 07e8adc3d9af36553a4bbf09465b18467bd6f045..267879aaaac3de92772d469d010779b5178fb12d 100644
--- a/ssoauth/views.py
+++ b/ssoauth/views.py
@@ -1,8 +1,9 @@
-from django.views.generic import View, FormView
+from django.views.generic import View, FormView, TemplateView, RedirectView
 from django import http
 from django import urls
 from django.utils.decorators import method_decorator
 from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.clickjacking import xframe_options_exempt
 from django.contrib import auth as contrib_auth
 from django.contrib.auth import models as contrib_auth_models
 from django.contrib.auth import REDIRECT_FIELD_NAME
@@ -10,6 +11,7 @@ from django import conf
 from django.core import exceptions
 from django import forms
 from django.views.decorators.cache import never_cache
+from django.contrib.sites.shortcuts import get_current_site
 from . import logger
 from . import app_settings
 from . import auth_utils
@@ -71,16 +73,48 @@ class LogInView(SAMLMixin, View):
         return str(next_url)
 
 
-class LogOutView(View):
+class LogOutView(RedirectView):
+    """
+    Logs the user out locally.
+    Redirects the user somewhere depending on the project settings.
+    """
 
-    def get(self, request, *args, **kwargs):
-        contrib_auth.logout(request)
-        return http.HttpResponseRedirect(self.get_next_url(request))
+    def get_redirect_url(self, *args, **kwargs):
+        # log the user out first, no matter SLO or not
+        contrib_auth.logout(self.request)
+        # decide where to redirect the user
+        url_next = self.request.GET.get(REDIRECT_FIELD_NAME, None)
+        settings = conf.settings.LOGOUT_REDIRECT_URL
+        default = urls.reverse_lazy("sso-logged-out-locally")
+        return url_next or settings or default
 
-    @staticmethod
-    def get_next_url(request):
-        next_url = request.GET.get(REDIRECT_FIELD_NAME, None) or conf.settings.LOGOUT_REDIRECT_URL or "/"
-        return str(next_url)
+
+class LoggedOutLocallyView(TemplateView):
+    """
+    If you use it, you should better override the default template. See: https://docs.djangoproject.com/en/1.11/howto/overriding-templates/
+    """
+    template_name = "ssoauth/logged_out_locally.html"
+
+    def dispatch(self, request, *args, **kwargs):
+        if self.request.user.is_authenticated:
+            # fool-proof
+            raise exceptions.PermissionDenied("This user is logged in!")
+        else:
+            return super().dispatch(request, *args, **kwargs)
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["IDP_LOGOUT_URL"] = app_settings.IDP_LOGOUT_URL
+        context["THIS_SITE"] = get_current_site(self.request)
+        return context
+
+class IdpLogoutRedirectView(RedirectView):
+
+    def get_redirect_url(self, *args, **kwargs):
+        if app_settings.IDP_LOGOUT_URL:
+            return http.HttpResponseRedirect(redirect_to=app_settings.IDP_LOGOUT_URL)
+        else:
+            raise exceptions.ImproperlyConfigured("IDP_LOGOUT_URL is not set.")
 
 
 @method_decorator(never_cache, "dispatch")
@@ -149,14 +183,46 @@ class ACSAuthNView(SAMLMixin, View):
 
 
 @method_decorator(never_cache, "dispatch")
-@method_decorator(csrf_exempt, "dispatch")
+@method_decorator(xframe_options_exempt, "dispatch")
 class SLSView(SAMLMixin, View):
     """
     SLS (Single Logout Service) binding.
     When this view is opened by a user (usually inside of a frame) this user must be unconditionally logged off.
     """
-    def get(self):
-        return None
+
+    def dispatch(self, request, *args, **kwargs):
+        if app_settings.SP_SLS_ENABLED:
+            return super().dispatch(request, *args, **kwargs)
+        else:
+            logger.warning("SLS is disabled.")
+            raise exceptions.PermissionDenied()
+
+    def get(self, *args, **kwargs):
+
+        def logout_callback():
+            nonlocal self
+            logger.warning("Logging out the user: {0}".format(self.request.user))
+            contrib_auth.logout(self.request)
+            # self.request.session.flush()
+
+        errors = list()
+        try:
+            auth = self.get_onelogin_auth(self.request)
+            response_url = auth.process_slo(delete_session_cb=logout_callback)
+            errors.extend(auth.get_errors())
+            if response_url:
+                response = http.HttpResponseRedirect(response_url)
+            else:
+                raise AssertionError("Did not get a response URL.")  # OneLogin toolkit fails to raise an exception
+        except Exception as e:
+            logger.error("SLO failed. {e.__class__.__name__}: {e}".format(e=e))
+            errors.append("{e.__class__.__name__}: {e}".format(e=e))
+            response = http.HttpResponseBadRequest(content="Bad SLS request.")
+        for error in errors:
+            logger.error(error)
+        if app_settings.SP_SLS_X_FRAME_OPTIONS:
+            response["X-Frame-Options"] = app_settings.SP_SLS_X_FRAME_OPTIONS
+        return response
 
 
 class MetadataView(SAMLMixin, View):
@@ -184,7 +250,7 @@ class DevView(FormView):
     form_class = DevForm
 
     def __init__(self, *args, **kwargs):
-        if not conf.settings.DEBUG and False:
+        if not conf.settings.DEBUG:
             raise exceptions.PermissionDenied()
         else:
             super().__init__(*args, **kwargs)