diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7da590064c8a1c08297461f57497145308c192c4..6b9a8483fe2c041f55254817dac0ce32b29211a3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,6 +19,17 @@ consent_provider:
       - consent_provider/**/*
       - .gitlab-ci.yml
 
+logout_provider:
+  stage: build
+  variables:
+    KANIKO_CONTEXT: "logout_provider"
+    KANIKO_BUILD_IMAGENAME: $CI_JOB_NAME
+  extends: .kaniko_build
+  only:
+    changes:
+      - logout_provider/**/*
+      - .gitlab-ci.yml
+
 login_provider:
   stage: build
   variables:
@@ -74,6 +85,8 @@ behave-integration:
       alias: login
     - name: ${CI_REGISTRY_IMAGE}/consent_provider:${CI_COMMIT_REF_NAME}
       alias: consent
+    - name: ${CI_REGISTRY_IMAGE}/logout_provider:${CI_COMMIT_REF_NAME}
+      alias: logout
     - name: oryd/hydra:latest
       alias: hydra
       command:
@@ -81,11 +94,11 @@ behave-integration:
         - all
         - --dangerous-force-http
         - --dangerous-allow-insecure-redirect-urls
-        - http://oauth:5000/login/sso/authorized
+        - "http://oidc:5000/callback"
     - name: open.greenhost.net:4567/openappstack/user-panel/backend:master
       alias: backend
     - name: ${CI_REGISTRY_IMAGE}/integration_test_app:${CI_COMMIT_REF_NAME}
-      alias: oauth
+      alias: oidc
   variables:
     # Feature Flag FF_NETWORK_PER_BUILD Enables creation of a docker network per build
     # with the docker executor of the gitlab-runner. This is required for service
@@ -93,9 +106,7 @@ behave-integration:
     FF_NETWORK_PER_BUILD: 1
     DATABASE_HOST: "postgres"
     URLS_LOGIN: "http://login:5000/login"
-    URLS_LOGOUT: "http://login:5000/logout"
-    LOGOUT_URL: "http://login/logout"
-    URLS_POST_LOGOUT_REDIRECT: "http://login:5000/"
+    URLS_LOGOUT: "http://logout:5002/logout"
     URLS_CONSENT: "http://consent:5001/consent"
     URLS_SELF_ISSUER: "http://hydra:4444/"
     BASE_URL: "http://hydra:4444/"
@@ -113,8 +124,6 @@ behave-integration:
     ROLE: "admin"
     DSN: "memory"
     SECRETS_SYSTEM: "youReallyNeedToChangeThis"
-    OIDC_SUBJECT_TYPES_SUPPORTED: "public,pairwise"
-    OIDC_SUBJECT_TYPE_PAIRWISE_SALT: "youReallyNeedToChangeThis"
     DATABASE_USER: postgres
     DATABASE_PASSWORD: secret
     DATABASE_NAME: postgres
@@ -137,12 +146,12 @@ behave-integration:
     - /bin/bash user-panel/backend/utils/assign-role.bash ${TESTUSER_USERNAME} ${ROLE} backend:5000
     # Wait for 60s for hydra to become available. Then create the oauth2 client object
     - while [[ $HYDRASTATUS -ne "200" &&  60 -ge $TIMER ]]; do HYDRASTATUS=`curl http://hydra:4445/health/ready -o /dev/null -w "%{http_code}"` || TIMER=$TIMER+5 && sleep 5 ; done
-    - /bin/bash test/create-hydra-client.bash ${KEY} ${SECRET} http://hydra:4445 http://oauth:5000/login/sso/authorized
+    - /bin/bash test/create-hydra-client.bash ${KEY} ${SECRET} http://hydra:4445 http://oidc:5000/callback http://oidc:5000/ http://oidc:5000/logout
     - cd test/integration_tests/test/behave/
     - >
         python3 -m behave
         -D headless=True
-        -D url=http://oauth:5000
+        -D url=http://oidc:5000
         -D username=${TESTUSER_USERNAME}
         -D username2=${TESTUSER_USERNAME2}
         -D password=${TESTUSER_PASSWORD}
diff --git a/docker-compose.yml b/docker-compose.yml
index 260a4125d2335e5b2685f2234e7d014f239e3a59..546bb2d6ca9b92e554342cdc968d7e9a47a276f3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,13 +13,12 @@ services:
       - "4445:4445" # Admin port
       - "5555:5555" # Port for hydra token user
     command:
-        serve all --dangerous-force-http --dangerous-allow-insecure-redirect-urls "http://127.0.0.1:13337/login/sso/authorized, http://localhost:3000/login/sso/authorized"
+      serve all --dangerous-force-http --dangerous-allow-insecure-redirect-urls "http://127.0.0.1:13337/callback, http://127.0.0.1:13337/"
     environment:
       - URLS_SELF_ISSUER=http://localhost:4444/
       - URLS_CONSENT=http://localhost:5001/consent
       - URLS_LOGIN=http://localhost:5000/login
-      - URLS_LOGOUT=http://localhost:5000/logout
-      - URLS_POST_LOGOUT_REDIRECT=http://localhost:5000/
+      - URLS_LOGOUT=http://localhost:5002/logout
       - DSN=memory
       - SECRETS_SYSTEM=youReallyNeedToChangeThis
       - OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise
@@ -37,6 +36,14 @@ services:
     ports:
       - "5001:5001"
     restart: unless-stopped
+  logout:
+    build: logout_provider/
+    environment:
+      - HYDRA_ADMIN_URL=http://hydra:4445
+      - FLASK_ENV=development
+    ports:
+      - "5002:5002"
+    restart: unless-stopped
   login:
     build: login_provider/
     environment:
@@ -56,7 +63,7 @@ services:
       - DATABASE_NAME=postgres
       - DATABASE_HOST=psql
     ports:
-      - "5002:5000"
+      - "5003:5000"
     restart: unless-stopped
   psql:
     image: postgres:11
@@ -68,16 +75,11 @@ services:
     build: ./test/integration_tests
     environment:
       - BASE_URL=http://localhost:4444/
-      - ACCESS_TOKEN_URL=http://hydra:4444/oauth2/token
-      - LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout
-      - AUTHORIZE_URL=http://localhost:4444/oauth2/auth
-      - USERINFO_URL=http://hydra:4444/userinfo
       - KEY=testapp
       - SECRET=secret
-      - OAUTHLIB_INSECURE_TRANSPORT=true
       - FLASK_ENV=development
     # with this settings run:
-    ## `bash test/create-hydra-client.bash testapp clientsecret http://localhost:4445 http://127.0.0.1:13337/login/sso/authorized
+    ## `bash test/create-hydra-client.bash testapp clientsecret http://localhost:4445 http://127.0.0.1:13337/callback http://127.0.0.1:13337 http:/127.0.0.1:13337/logout
     ## to register a corresponding oauth client with hydra
     ports:
       - "13337:13337"
diff --git a/logout_provider/Dockerfile b/logout_provider/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..c69128269214933445c04dae07300b4c71de0992
--- /dev/null
+++ b/logout_provider/Dockerfile
@@ -0,0 +1,22 @@
+FROM python:3.7-alpine
+
+RUN apk add gcc libc-dev libffi-dev
+
+WORKDIR /usr/src/app
+
+COPY requirements.txt ./
+RUN pip install --no-cache-dir -r requirements.txt
+
+COPY . .
+
+ARG FLASK_PORT=5002
+
+EXPOSE $FLASK_PORT
+
+ENV FLASK_ENV production
+ENV FLASK_RUN_PORT $FLASK_PORT
+ENV FLASK_RUN_HOST 0.0.0.0
+ENV HYDRA_ADMIN_URL http://localhost:4445
+
+
+CMD [ "flask", "run" ]
diff --git a/logout_provider/app.py b/logout_provider/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..e17cebc77d81c2eba9278abcb26701b2e64cc0db
--- /dev/null
+++ b/logout_provider/app.py
@@ -0,0 +1,44 @@
+from flask import abort, Flask, redirect, request
+from flask.views import View
+from hydra_client import HydraAdmin
+import hydra_client
+import logging
+from os import environ
+
+HYDRA_ADMIN_URL = environ['HYDRA_ADMIN_URL']
+HYDRA = HydraAdmin(HYDRA_ADMIN_URL)
+
+app = Flask(__name__)
+app.logger.setLevel(logging.INFO)
+
+@app.route('/logout', methods=['GET'])
+def home():
+    """Handles the OpenID Connect Logout flow
+
+    Communicates with the hydra server to start the logout flow which uses backchannel and
+    frontchannel logout methods to log out the user from all applications they have
+    access to.
+
+    Args:
+        logout_challenge: Reference to a logout challenge object in form of an alphanumeric
+         String. Can be used to retrieve the LogoutRequest object via the Hydra Admin API (GET)
+
+    Returns:
+        Redirect to the url that is provided by the LogoutRequest object.
+    """
+    challenge = request.args.get("logout_challenge")
+    app.logger.info("Logout request: challenge={0}".format(challenge))
+    if not challenge:
+        abort(403)
+    try:
+        logout_request = HYDRA.logout_request(challenge)
+    except hydra_client.exceptions.NotFound:
+        app.logger.error("Not Found. Logout request not found. challenge={0}".format(challenge))
+        abort(404)
+    except hydra_client.exceptions.HTTPError:
+        app.logger.error("Conflict. Logout request has been used already. challenge={0}".format(challenge))
+        abort(503)
+    return redirect(logout_request.accept(subject=logout_request.subject))
+
+if __name__ == '__main__':
+    app.run()
diff --git a/logout_provider/requirements.txt b/logout_provider/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..435993d1d1d480f9f99bbac5ab7d32be2861099c
--- /dev/null
+++ b/logout_provider/requirements.txt
@@ -0,0 +1,2 @@
+Flask
+hydra-client
diff --git a/test/create-hydra-client.bash b/test/create-hydra-client.bash
index 8b4c78fd169d335a52483f820b53db9a1cff75ae..78017032e230e1e12d7c19c196294717a9f96ce5 100755
--- a/test/create-hydra-client.bash
+++ b/test/create-hydra-client.bash
@@ -9,9 +9,14 @@ KEY=$1
 SECRET=$2
 HOST_URL=$3
 REDIRECT_URI=$4
+POST_LOGOUT_REDIRECT_URI=$5
+FRONTCHANNEL_LOGOUT_URI=$6
 SCOPES="openid profile email openappstack_roles"
 
+curl --request DELETE \
+    $HOST_URL/clients/$KEY;
+
 curl --header "Content-Type: application/json" \
     --request POST \
-    --data "{\"client_id\": \"$KEY\", \"client_name\": \"$KEY\", \"client_secret\": \"$SECRET\", \"redirect_uris\": [\"$REDIRECT_URI\"], \"scope\": \"$SCOPES\", \"grant_types\": [\"authorization_code\",\"refresh_token\"], \"response_types\": [\"code\"], \"token_endpoint_auth_method\": \"client_secret_basic\"}" \
+    --data "{\"client_id\": \"$KEY\", \"client_name\": \"$KEY\", \"client_secret\": \"$SECRET\", \"frontchannel_logout_uri\": \"$FRONTCHANNEL_LOGOUT_URI\", \"post_logout_redirect_uris\": [\"$POST_LOGOUT_REDIRECT_URI\"], \"redirect_uris\": [\"$REDIRECT_URI\"], \"scope\": \"$SCOPES\", \"grant_types\": [\"authorization_code\",\"refresh_token\"], \"response_types\": [\"code\", \"token id_token\"], \"token_endpoint_auth_method\": \"client_secret_basic\"}" \
     $HOST_URL/clients
diff --git a/test/integration_tests/Dockerfile b/test/integration_tests/Dockerfile
index 9dbbf62141109039333c9c3b80a3cb201477cc09..2692e305ec6fc2ff62325a8fb8a4811cfbc9ad25 100644
--- a/test/integration_tests/Dockerfile
+++ b/test/integration_tests/Dockerfile
@@ -3,13 +3,19 @@ FROM python:3.7-alpine
 WORKDIR /usr/src/app
 
 COPY requirements.txt ./
+
+RUN apk --no-cache add \
+    libffi-dev \
+    python3-dev \
+    libc-dev \
+    openssl-dev \
+    gcc
+
 RUN pip3 install --no-cache-dir -r requirements.txt
 
 COPY . .
 
 ENV BASE_URL https://sso.oas.example.net
-ENV ACCESS_TOKEN_URL https://sso.oas.example.net/oauth2/token
-ENV AUTHORIZE_URL https://sso.oas.example.net/oauth2/auth
 ENV KEY testapp
 ENV SECRET verysecret
 
diff --git a/test/integration_tests/README.md b/test/integration_tests/README.md
index 431c297a5fd087df9382719fcf960d49ae8eeb78..83e20abeac0ded3b2351baeb6b3d90d6b82f670d 100644
--- a/test/integration_tests/README.md
+++ b/test/integration_tests/README.md
@@ -7,16 +7,27 @@ The single sign-on application needs to be up and running. Instructions on how t
 that can be found in `../../README.md`.
 
 ### Create oAuth Client
-You also need to create an oAuth2-Client to enable this application to communicate with the 
+You also need to create an oAuth2-Client to enable this application to communicate with the
 oAuth server.
 
 To do that you can run the `create-hydra-client` script in this repository:
 ```
-bash ../create-hydra-client.bash testapplication clientsecret http://localhost:4445 http://localhost:13337/callback
+bash ../create-hydra-client.bash testapplication clientsecret http://localhost:4445 http://localhost:13337/callback http://localhost:13337/ http://localhost:13337/logout
 ```
+
 `http://localhost:4445` refers to the hydra-admin service. `http://localhost:13337/callback` is the
-callback address of the test application. Make sure it matches the address specified in `../../docker-compose.yml`
-> `serve all --dangerous-force-http --dangerous-allow-insecure-redirect-urls "http://localhost:13337/callback":`
+callback uri of the test application. The third uri (http://localhost:13337) will be used as a post
+logout redirect uri. The agent is forwarded to this address after the single-sign-off process finishes.
+The last argument specifies the uri that is used to trigger a
+[OIDC frontchanel logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html#ExampleFrontchannel).
+
+
+Make sure that the callback url you specified as the 4th argument when executing the
+`create-hydra-client.bash` script exactly matches one of the insecure
+redirect urls that are specified in `../../docker-compose.yml` (line 16). The argument to look
+out for is called `--dangerous-allow-insecure-redirect-urls`:
+
+> `serve all --dangerous-force-http --dangerous-allow-insecure-redirect-urls "http://localhost:13337/callback"`
 
 ### Create users
 
@@ -47,13 +58,8 @@ if you run your setup locally.
 
 ```
 export BASE_URL=http://localhost:4444/                         # Hydra public API Base
-export ACCESS_TOKEN_URL=http://localhost:4444/oauth2/token     # Hydra token endpoint
-export LOGOUT_URL=http://localhost:4444/oauth2/sessions/logout # Hydra logout endpoint
-export AUTHORIZE_URL=http://localhost:4444/oauth2/auth         # Hydra authentication endpoint
-export USERINFO_URL=http://localhost:4444/userinfo             # Hydra OpenID Connect userinfo endpoint
 export KEY=testapplication                                     # name of your oauth/openID Connect client (application)
 export SECRET=clientsecret                                     # secret of your oauth/openID Connect client (application)
-export OAUTHLIB_INSECURE_TRANSPORT=true                        # allows connections via http
 ```
 
 
diff --git a/test/integration_tests/app.py b/test/integration_tests/app.py
index 9fdbc42e85d0c67ec846cd1ae7bb1b9950fd0b8f..61577d95ee222a34341edd9a60b3be9115f99e09 100644
--- a/test/integration_tests/app.py
+++ b/test/integration_tests/app.py
@@ -1,70 +1,83 @@
-from flask import Flask, url_for, redirect, jsonify
 import json
 from os import environ
-from flask_dance.consumer import OAuth2ConsumerBlueprint, oauth_authorized, oauth_error
+from flask import Flask, url_for, redirect, jsonify, session, request
+from oic.oic import Client
+from oic.oic import RegistrationResponse
+from oic.oic.message import AuthorizationResponse
+from oic.utils.authn.client import CLIENT_AUTHN_METHOD
+from oic import rndstr
 
 
-BASE_URL=environ["BASE_URL"]
-ACCESS_TOKEN_URL=environ["ACCESS_TOKEN_URL"]
-AUTHORIZE_URL=environ["AUTHORIZE_URL"]
-USERINFO_URL=environ["USERINFO_URL"]
-KEY=environ["KEY"]
-SECRET=environ["SECRET"]
+BASE_URL = environ["BASE_URL"]
+KEY = environ["KEY"]
+SECRET = environ["SECRET"]
 
 app = Flask(__name__)
 app.secret_key = 'development'
 
-sso = OAuth2ConsumerBlueprint(
-    "sso", __name__,
-    client_id=KEY,
-    client_secret=SECRET,
-    base_url=BASE_URL,
-    token_url=ACCESS_TOKEN_URL,
-    authorization_url=AUTHORIZE_URL,
-    )
+sso = Client(client_authn_method=CLIENT_AUTHN_METHOD)
+sso.provider_config(BASE_URL)
+sso_client_auth = {"client_id": KEY, "client_secret": SECRET}
+sso_auth_args = {
+    "client_id": KEY,
+    "response_type": ["code"],
+    "scope": ["openid", "profile", "email", "openappstack_roles"],
+    "nonce": "",
+    "state": "",
+    "redirect_uri": ""
+    }
 
-sso.error = {}
-
-app.register_blueprint(sso, url_prefix="/login")
+sso_client_reg = RegistrationResponse(**sso_client_auth)
+sso.store_registration_info(sso_client_reg)
 
 @app.route("/")
 def index():
-    if sso.error:
-        errorpage = jsonify(sso.error)
-        sso.error = {}
-        return errorpage
-    elif not sso.token:
-        return redirect(url_for("sso.login"))
-    else:
-        return jsonify(sso.token)
+    if "state" not in session.keys():
+        state = rndstr()
+        session["state"] = state
+    if session["state"] not in sso.grant.keys() or not sso.grant[session["state"]].is_valid():
+        sso_auth_args["nonce"] = rndstr()
+        sso_auth_args["state"] = session["state"]
+        sso_auth_args["redirect_uri"] = url_for("callback", _external=True)
+        sso_auth_request = sso.construct_AuthorizationRequest(request_args=sso_auth_args)
+        return redirect(sso_auth_request.request(sso.authorization_endpoint))
+    token = sso.grant[session["state"]].get_token()
+    return jsonify({"access_token": token.access_token})
 
 @app.route("/userinfo")
 def info():
-    if sso.authorized:
-        resp = sso.session.get(USERINFO_URL)
-        assert resp.ok
-        return jsonify(json.loads(resp.content.decode()))
-    else:
-        return redirect("/")
+    if "state" in session.keys() and sso.grant[session["state"]].is_valid:
+        token = sso.grant[session["state"]].get_token()
+        return jsonify(token.id_token.to_dict())
+    return redirect("/")
 
 @app.route("/logout")
 def logout():
-    if sso.authorized:
-        del sso.token
-        return redirect("/")
-    else:
-        return redirect("/")
+    del sso.grant[session["state"]]
+    del session["state"]
+    return redirect("/")
 
-@oauth_authorized.connect
-def save_token(blueprint, token):
-    # set OAuth token in the token storage backend
-    blueprint.token = token
+@app.route("/single-sign-off")
+def single_sign_off():
+    redir = sso.construct_CheckSessionRequest(state=session["state"])
+    answer = sso.do_end_session_request(state=session["state"], request_args={
+        "id_token_hint": redir["id_token"],
+        "state": session["state"],
+        "post_logout_redirect_uri":  url_for("index", _external=True)
+        })
+    return redirect(answer.url)
 
-@oauth_error.connect
-def save_error(blueprint, **error):
-    # set error in the token storage backend
-    blueprint.error = error
+@app.route("/callback")
+def callback():
+    if "error" in request.args:
+        return jsonify(request.args)
+    aresp = sso.parse_response(AuthorizationResponse, info=json.dumps(request.args))
+    assert aresp["state"] == session["state"]
+    token = sso.do_access_token_request(
+        state=aresp["state"],
+        request_args={"code": aresp["code"]})
+    sso.store_response(session["state"], token)
+    return redirect(url_for("index"))
 
 if __name__ == "__main__":
     app.run()
-
diff --git a/test/integration_tests/requirements.txt b/test/integration_tests/requirements.txt
index 40df930d93c0823aea07df942a1b80231bf38ace..2d579111be56d50870a2d3723b11a42ab1e22c58 100644
--- a/test/integration_tests/requirements.txt
+++ b/test/integration_tests/requirements.txt
@@ -1,3 +1,3 @@
 Flask
-Flask-Dance
-blinker
+oic
+Flask-Session
diff --git a/test/integration_tests/test/behave/features/environment.py b/test/integration_tests/test/behave/features/environment.py
index fe5e4cb26edb3b4aaa73ae8d328fa6878b6c7ac8..dddff08475705b1e9ff63f42c2cada2e848ca92c 100644
--- a/test/integration_tests/test/behave/features/environment.py
+++ b/test/integration_tests/test/behave/features/environment.py
@@ -40,6 +40,7 @@ def before_tag(context, tag):
     values['home'] = userdata.get('url')
     values['logout'] = values['home'] + "/logout"
     values['userinfo'] = values['home'] + "/userinfo"
+    values['single-sign-off'] = values['home'] + "/single-sign-off"
     values['username'] = userdata.get('username')
     values['username2'] = userdata.get('username2')
     values['password'] = userdata.get('password')
diff --git a/test/integration_tests/test/behave/features/logout.feature b/test/integration_tests/test/behave/features/logout.feature
new file mode 100644
index 0000000000000000000000000000000000000000..6df862eebbd57eabee0a95b48fc1878e08bebb27
--- /dev/null
+++ b/test/integration_tests/test/behave/features/logout.feature
@@ -0,0 +1,20 @@
+@oauth
+Feature: Test logout-provider function
+    As an OAS user
+    I want to be able to use single-sign off triggered by an
+    OAS APP. And verify that even though I selected remember me
+    my session was removed.
+
+Scenario: Login with a valid user and remember session
+    Given the oauth client "home" URL was opened
+    And the element "input#remember" is visible
+    When I enter the "username" in the inputfield "input#username"
+    And I enter the "password" in the inputfield "input#password"
+    And I click on the element "input#remember"
+    And I click on the button "input#submit"
+    Then I wait on element "input#password" for 1000ms to not exist
+
+Scenario: Logout using the single sign-off feature
+    Given the oauth client "single-sign-off" URL was opened
+    And I pause for 1000ms
+    Then I wait on element "input#username" for 1000ms to exist
diff --git a/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature b/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature
index e4b6150bc599cd68e37ad2c57cd1386b61893c93..fb1c0639fa48165707bfce35c402717a6d26d4a8 100644
--- a/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature
+++ b/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature
@@ -12,7 +12,6 @@ Scenario: Login with a valid user without access to an application
     And I click on the button "input#submit"
     Then I wait on element "input#password" for 1000ms to not exist
     And I expect that element "input#username" does not exist
-    And I expect that the path is "/"
     And I expect that element "body" contains the text "error"
     And I expect that element "body" contains the text "Permission denied"
     And I expect that element "body" contains the text "missing application permission"
@@ -25,7 +24,6 @@ Scenario: Login with an invalid user
     And I click on the button "input#submit"
     Then I wait on element "input#password" for 1000ms to not exist
     And I expect that element "input#username" does not exist
-    And I expect that the path is "/"
     And I expect that element "body" contains the text "error"
     And I expect that element "body" contains the text "Login denied"
     And I expect that element "body" contains the text "Invalid username or password"