diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e5c2a358cd369b570a63ee3eb8c6bf4015c1e5da..7da590064c8a1c08297461f57497145308c192c4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,7 +38,7 @@ integration_test_app:
   extends: .kaniko_build
   only:
     changes:
-      - ./test/integration_tests/**/*
+      - test/integration_tests/**/*
       - .gitlab-ci.yml
 
 integration_test:
@@ -49,7 +49,7 @@ integration_test:
   extends: .kaniko_build
   only:
     changes:
-      - ./test/integration_tests/test/**/*
+      - test/integration_tests/test/**/*
       - .gitlab-ci.yml
 
 pylint:
@@ -81,7 +81,7 @@ behave-integration:
         - all
         - --dangerous-force-http
         - --dangerous-allow-insecure-redirect-urls
-        - http://oauth:5000/callback
+        - http://oauth:5000/login/sso/authorized
     - name: open.greenhost.net:4567/openappstack/user-panel/backend:master
       alias: backend
     - name: ${CI_REGISTRY_IMAGE}/integration_test_app:${CI_COMMIT_REF_NAME}
@@ -137,7 +137,7 @@ 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/callback
+    - /bin/bash test/create-hydra-client.bash ${KEY} ${SECRET} http://hydra:4445 http://oauth:5000/login/sso/authorized
     - cd test/integration_tests/test/behave/
     - >
         python3 -m behave
diff --git a/docker-compose.yml b/docker-compose.yml
index 8ed729d76d821a2449a56e26fec6ddc442329f45..260a4125d2335e5b2685f2234e7d014f239e3a59 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,7 +13,7 @@ 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/callback, http://localhost:3000/callback"
+        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"
     environment:
       - URLS_SELF_ISSUER=http://localhost:4444/
       - URLS_CONSENT=http://localhost:5001/consent
@@ -25,7 +25,8 @@ services:
       - OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise
       - OIDC_SUBJECT_TYPE_PAIRWISE_SALT=youReallyNeedToChangeThis
       - SERVE_PUBLIC_CORS_DEBUG=true
-      - LOG_LEVEL="debug"
+      - LOG_LEVEL=debug
+      - LOG_LEAK_SENSITIVE_VALUES=true
     restart: unless-stopped
   consent:
     build: consent_provider/
@@ -76,8 +77,8 @@ services:
       - OAUTHLIB_INSECURE_TRANSPORT=true
       - FLASK_ENV=development
     # with this settings run:
-    # `bash test/create-127.0.0.1-client.bash testapp clientsecret http://localhost:4445 http://127.0.0.1:13337/callback
-    # to register a corresponding oauth client with hydra
+    ## `bash test/create-hydra-client.bash testapp clientsecret http://localhost:4445 http://127.0.0.1:13337/login/sso/authorized
+    ## to register a corresponding oauth client with hydra
     ports:
       - "13337:13337"
     command: flask run --port 13337 --host "0.0.0.0"
diff --git a/test/create-hydra-client.bash b/test/create-hydra-client.bash
index 1d0fc27e157f2614c55bda53a1200da417c0b9e1..8b4c78fd169d335a52483f820b53db9a1cff75ae 100755
--- a/test/create-hydra-client.bash
+++ b/test/create-hydra-client.bash
@@ -13,5 +13,5 @@ SCOPES="openid profile email openappstack_roles"
 
 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_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\"}" \
     $HOST_URL/clients
diff --git a/test/integration_tests/app.py b/test/integration_tests/app.py
index e58a728047e9987b01eee542955a7b48e8f38bd9..9fdbc42e85d0c67ec846cd1ae7bb1b9950fd0b8f 100644
--- a/test/integration_tests/app.py
+++ b/test/integration_tests/app.py
@@ -1,14 +1,11 @@
-from flask import Flask, abort, url_for, redirect, request, session, jsonify
-from werkzeug import security
-from json import dumps
+from flask import Flask, url_for, redirect, jsonify
+import json
 from os import environ
-from flask_oauthlib.client import OAuth
-import uuid
+from flask_dance.consumer import OAuth2ConsumerBlueprint, oauth_authorized, oauth_error
 
 
 BASE_URL=environ["BASE_URL"]
 ACCESS_TOKEN_URL=environ["ACCESS_TOKEN_URL"]
-LOGOUT_URL=environ["LOGOUT_URL"]
 AUTHORIZE_URL=environ["AUTHORIZE_URL"]
 USERINFO_URL=environ["USERINFO_URL"]
 KEY=environ["KEY"]
@@ -17,54 +14,57 @@ SECRET=environ["SECRET"]
 app = Flask(__name__)
 app.secret_key = 'development'
 
-oauth = OAuth(app)
-
-sso = oauth.remote_app(
-    "sso",
+sso = OAuth2ConsumerBlueprint(
+    "sso", __name__,
+    client_id=KEY,
+    client_secret=SECRET,
     base_url=BASE_URL,
-    request_token_url=None,
-    access_token_url=ACCESS_TOKEN_URL,
-    authorize_url=AUTHORIZE_URL,
-    consumer_key=KEY,
-    request_token_params={'state': lambda: security.gen_salt(10), "scope": "openid profile email openappstack_roles"},
-    consumer_secret=SECRET)
+    token_url=ACCESS_TOKEN_URL,
+    authorization_url=AUTHORIZE_URL,
+    )
+
+sso.error = {}
 
-@sso.tokengetter
-def get_sso_token():
-    return session.get('sso_token')
+app.register_blueprint(sso, url_prefix="/login")
 
-@app.route('/')
-def login():
-    return sso.authorize(url_for('callback', _external=True))
+@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)
 
-@app.route('/userinfo')
-def get_userinfo():
-    if "id_token" in session:
-        # Use the id_token to retrieve user information from the sso server
-        resp = sso.request(USERINFO_URL)
-        return jsonify(resp.data)
-    abort(403)
+@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("/")
 
-@app.route('/logout')
+@app.route("/logout")
 def logout():
-    del session['sso_token']
-    return redirect(url_for('login'))
+    if sso.authorized:
+        del sso.token
+        return redirect("/")
+    else:
+        return redirect("/")
 
-@app.route('/callback')
-def callback():
-    # retrieve all sso related values from the response
-    resp = sso.authorized_response()
-    if resp is None:
-        # Response was not authorized. It doesn't contain any authorized values
-        if "error" in request.args:
-            return jsonify(request.args)
-        abort(403)
-    # The authorized response contains tokens that we can use to authenticate
-    # In order for the agent to do that we save the tokens in the user session
-    session['sso_token'] = (resp['access_token'],None)
-    if "id_token" in resp:
-        session['id_token'] = resp['id_token']
-    return jsonify(resp)
+@oauth_authorized.connect
+def save_token(blueprint, token):
+    # set OAuth token in the token storage backend
+    blueprint.token = token
+
+@oauth_error.connect
+def save_error(blueprint, **error):
+    # set error in the token storage backend
+    blueprint.error = error
 
 if __name__ == "__main__":
     app.run()
+
diff --git a/test/integration_tests/requirements.txt b/test/integration_tests/requirements.txt
index 065d7e389ee6f83b5684639db175728a511c694d..40df930d93c0823aea07df942a1b80231bf38ace 100644
--- a/test/integration_tests/requirements.txt
+++ b/test/integration_tests/requirements.txt
@@ -1,3 +1,3 @@
 Flask
-Flask-OAuthlib
-Werkzeug==0.16.1 # Due to bug: cannot find url_quote https://stackoverflow.com/questions/60192172/importerror-cannot-import-name-filestorage-from-werkzeug. Can be fixed by migrating from flask-oauthlib to oauthlib.
+Flask-Dance
+blinker
diff --git a/test/integration_tests/test/behave/features/login.feature b/test/integration_tests/test/behave/features/login.feature
index 93da3043866eb0cff6066964de101c2ca04c8195..86a132aeecc50f22fb5a758687a1d8d53c93685d 100644
--- a/test/integration_tests/test/behave/features/login.feature
+++ b/test/integration_tests/test/behave/features/login.feature
@@ -11,7 +11,7 @@ Scenario: Open the oAuth application and Login witha valid user
     And I enter the "password" in the inputfield "input#password"
     And I click on the button "input#submit"
     Then I wait on element "input#password" for 1000ms to not exist
-    And I expect that the path is "/callback"
+    And I expect that the path is "/"
     And I expect that element "body" contains the text "access_token"
 
 Scenario: Get OpenID Connect userdata for testuser
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 c95b995944b5ffd4d59cea4033981a9d789f180e..e4b6150bc599cd68e37ad2c57cd1386b61893c93 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,7 @@ 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 "/callback"
+    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 +25,7 @@ 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 "/callback"
+    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"
diff --git a/test/integration_tests/test/behave/features/remember_me.feature b/test/integration_tests/test/behave/features/remember_me.feature
index 81ad49e2abfc9cad524e7429239fa5a3767eb8b7..d5b3e53cd76e0f4cd2fa91831913a1333e96b564 100644
--- a/test/integration_tests/test/behave/features/remember_me.feature
+++ b/test/integration_tests/test/behave/features/remember_me.feature
@@ -14,7 +14,7 @@ Scenario: Login with a valid user and remember session
     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 "/callback"
+    And I expect that the path is "/"
     And I expect that element "body" contains the text "access_token"
 
 Scenario: Login without providing credentials
@@ -25,7 +25,7 @@ Scenario: Login without providing credentials
     And the element "button#continue" is visible
     When I click on the element "button#continue"
     Then I wait on element "button#continue" for 1000ms to not exist
-    And I expect that the path is "/callback"
+    And I expect that the path is "/"
     And I expect that element "body" contains the text "access_token"
 
 Scenario: Terminate single sign-on session