diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3281490e9ad75f80198b9e547edf0f24ffebb86..fdea5a7c927d7ab8bc2d1f7146db077a655fbb1d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## [0.8.3]
+
+- Introduce backend code for resetting 2FA, and add cli command for that.
+- Upgrade Kratos api library `ory-kratos-client` to 1.0.0.
+- Patch our usage of Kratos api pagination of identities list.
+
 ## [0.8.2]
 
 - End the Kratos session in prelogout. This makes sure that we end the "SSO
diff --git a/backend/areas/users/user_service.py b/backend/areas/users/user_service.py
index d899e3ea5de3150f79e6387106340160462aa790..b5e7681e86340108734bb43c3f0a08b1568ba109 100644
--- a/backend/areas/users/user_service.py
+++ b/backend/areas/users/user_service.py
@@ -22,10 +22,13 @@ kratos_identity_api = identity_api.IdentityApi(kratos_client)
 class UserService:
     @staticmethod
     def get_users():
-        page = 1
+        page = 0
         userList = []
-        while page > 0:
-            res = KratosApi.get("/admin/identities?per_page=1000&page={}".format(page)).json()
+        while page >= 0:
+            if page == 0:
+                res = KratosApi.get("/admin/identities?per_page=1000").json()
+            else:
+                res = KratosApi.get("/admin/identities?per_page=1000&page={}".format(page)).json()
             for r in res:
                 # removed the app role assignment function, passing simple user data
                 # userList.append(UserService.__insertAppRoleToUser(r["id"], r))
diff --git a/backend/helpers/kratos_user.py b/backend/helpers/kratos_user.py
index d5f50d1120713a85c2444cb4892c7c4e8fc8398b..13a10e3a2f703cf86ac21c156154c98709236c47 100644
--- a/backend/helpers/kratos_user.py
+++ b/backend/helpers/kratos_user.py
@@ -133,9 +133,12 @@ class KratosUser():
         kratos_id = None
 
         # Get out user ID by iterating over all available IDs
-        page = 1
-        while page > 0:
-            data = api.list_identities(per_page=1000, page=page)
+        page = 0
+        while page >= 0:
+            if page == 0:
+                data = api.list_identities(per_page=1000)
+            else:
+                data = api.list_identities(per_page=1000, page=page)
             for kratos_obj in data:
                 # Unique identifier we use
                 if kratos_obj.traits['email'] == email:
@@ -158,9 +161,12 @@ class KratosUser():
         kratos_id = None
         return_list = []
         # Get out user ID by iterating over all available ID
-        page = 1
-        while page > 0:
-            data = api.list_identities(per_page=1000, page=page)
+        page = 0
+        while page >= 0:
+            if page == 0:
+                data = api.list_identities(per_page=1000)
+            else:
+                data = api.list_identities(per_page=1000, page=page)
             for kratos_obj in data:
                 kratos_id = str(kratos_obj.id)
                 return_list.append(KratosUser(api, kratos_id))
diff --git a/backend/requirements.txt b/backend/requirements.txt
index fc2836ad096a8f50d217569cc0a9b8eccdc5db34..246dcc78d94c34198ec9ed1448b638a08be62280 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -23,7 +23,7 @@ MarkupSafe==2.1.1
 mypy-extensions==0.4.3
 NamedAtomicLock==1.1.3
 oauthlib==3.2.0
-ory-kratos-client==0.11.0
+ory-kratos-client==1.0.0
 ory-hydra-client==1.11.8
 pathspec==0.9.0
 platformdirs==2.5.1
diff --git a/backend/web/login/login.py b/backend/web/login/login.py
index 71117746cc5f5e7f27243c78dca1a3cf18d11bf0..627f8ec1acbd09d453862de507a3bb7cfd99cef0 100644
--- a/backend/web/login/login.py
+++ b/backend/web/login/login.py
@@ -560,19 +560,28 @@ def get_kratos_cookie():
     return cookie
 
 
-
-@web.route("/prelogout", methods=["GET"])
-def prelogout():
-    """Handles the Hydra OpenID Connect Logout flow
+@web.route("/logout", methods=["GET"])
+def logout():
+    """Handles the Hydra OpenID Connect Logout flow and Kratos logout flow.
 
     Steps:
     1. Hydra's /oauth2/sessions/logout endpoint is called by an application
     2. Hydra calls this endpoint with a `logout_challenge` get parameter
     3. We retrieve the logout request using the challenge
-    4. We accept the Hydra logout request
-    5. We redirect to Hydra to clean-up cookies.
-    6. Hyrda calls back to us with a post logout handle (/logout)
-
+    4. We accept the Hydra logout request. This returns a URL -- let's call it
+    "next redirect" -- that we should redirect the browser to to finish the
+    Hydra logout.
+    5. We create a Kratos logout flow, setting its `return_to` parameter to the
+    "next redirect". This returns a Kratos logout URL.
+    6. We return a small HTML page to the browser, based on the `clear.html`
+    template, which clears dashboard local storage and redirects to the Kratos
+    logout URL.
+    7. The browser follows that redirect, Kratos does its thing and redirects
+    to the "next redirect".
+    8. The browser follows the "next redirect", Hydra does its thing and
+    redirects to the "post-logout URL". We set that to the dashboard root URL
+    by default, but OIDC clients can override it to something else. For
+    example, Nextcloud sets it to the root Nextcloud URL.
      
     Args:
         logout_challenge (string): Reference to a Hydra logout challenge object
@@ -595,7 +604,7 @@ def prelogout():
         current_app.logger.error(
             "Conflict. Logout request with challenge '%s' has been used already.",
             challenge)
-        abort(503)
+        abort(404, "Logout request has been accepted already.")
 
     current_app.logger.info("Logout request hydra, subject %s", logout_request.subject)
 
@@ -604,20 +613,28 @@ def prelogout():
     # browser flow and we can't do both.
     try:
         hydra_return = hydra_admin_api.accept_logout_request(challenge)
+        next_redirect = hydra_return.redirect_to
     except Exception as ex:
-        current_app.logger.info("Error logging out hydra: %s", str(ex))
+        current_app.logger.info("Error accepting hydra logout request: %s", str(ex))
+        next_redirect = DASHBOARD_URL
 
-    # Now start ending the kratos session.
+    # Now end the kratos session.
     kratos_cookie = get_kratos_cookie()
     if not kratos_cookie:
         # No kratos cookie, already logged out from kratos.
         current_app.logger.info("Expected kratos cookie but not found. Redirecting to hydra post-logout");
-        return redirect(hydra_post_logout)
+        # We skip the Kratos logout, but we still need to follow
+        # `next_redirect` -- probably the Hydra logout URL -- and clear
+        # dashboard storage.
+        return render_template("clear.html",
+            url=next_redirect)
     try:
         # Create a Logout URL for Browsers
-        kratos_api_response = \
-            admin_frontend_api.create_browser_logout_flow(
-                cookie=kratos_cookie)
+        current_app.logger.info(f"Creating logout flow, with return_to={next_redirect}")
+        kratos_api_response = admin_frontend_api.create_browser_logout_flow(
+            return_to=next_redirect,
+            cookie=kratos_cookie)
+        current_app.logger.info("Kratos api response to creating logout flow:")
         current_app.logger.info(kratos_api_response)
         return render_template("clear.html",
             url=kratos_api_response.logout_url)
@@ -625,41 +642,7 @@ def prelogout():
         current_app.logger.error("Exception when calling"
             " create_browser_logout_flow: %s\n",
             ex)
-
-    current_app.logger.info("Hydra logout not completed. Redirecting to kratos logout, maybe user removed cookies manually")
-    return redirect("logout")
-
-
-@web.route("/logout", methods=["GET"])
-def logout():
-    """Handles the Kratos Logout flow
-
-    Steps:
-    1. We got here from hyrda
-    2. We retrieve the Kratos cookie from the browser
-    3. We generate a Kratos logout URL
-    4. We redirect to the Kratos logout URIL
-    """
-
-    kratos_cookie = get_kratos_cookie()
-    if not kratos_cookie:
-        # No kratos cookie, already logged out
-        current_app.logger.info("Expected kratos cookie but not found. Redirecting to login");
-        return render_template("clear.html",
-            url="login")
-
-    try:
-        # Create a Logout URL for Browsers
-        kratos_api_response = \
-            admin_frontend_api.create_browser_logout_flow(
-                cookie=kratos_cookie)
-        current_app.logger.info(kratos_api_response)
-    except ory_kratos_client.ApiException as ex:
-        current_app.logger.error("Exception when calling"
-            " create_self_service_logout_flow_url_for_browsers: %s\n",
-            ex)
-    return render_template("clear.html",
-            url=kratos_api_response.logout_url)
+        return redirect(DASHBOARD_URL)
 
 
 if DEMO_INSTANCE:
diff --git a/backend/web/templates/clear.html b/backend/web/templates/clear.html
index c1a37ddbc0c3462c3173dc5c02a4e80a392b1655..e7f06a7769ac9ebd73dda9f6685b29ee7e3d4bde 100644
--- a/backend/web/templates/clear.html
+++ b/backend/web/templates/clear.html
@@ -6,9 +6,9 @@
     // Wipe the local storage
     localStorage.removeItem("persist:root");
     // Redirect
-    window.location = '{{ url }}';
+    window.location = '{{ url | safe }}';
 </script>
 
-Redirecting ...
+Clearing session data and redirecting...
 
 {% endblock %}
diff --git a/deployment/helmchart/CHANGELOG.md b/deployment/helmchart/CHANGELOG.md
index 5a900410217645bb8c03090b1f389b473b6e077c..c75b0d57c2de8b282a748beba041d7b60833d76c 100644
--- a/deployment/helmchart/CHANGELOG.md
+++ b/deployment/helmchart/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## [1.8.3]
+
+* Update dashboard to version 0.8.3.
+
 ## [1.8.2]
 
 * Update dashboard to version 0.8.2.
diff --git a/deployment/helmchart/Chart.yaml b/deployment/helmchart/Chart.yaml
index 3e603f9912eedf2569267bb57af9061f0f1e82f3..c2554d29cfd24bdb4f00f7c60b466f757247473d 100644
--- a/deployment/helmchart/Chart.yaml
+++ b/deployment/helmchart/Chart.yaml
@@ -1,7 +1,7 @@
 annotations:
   category: Dashboard
 apiVersion: v2
-appVersion: 0.8.2
+appVersion: 0.8.3
 dependencies:
   - name: common
     # https://artifacthub.io/packages/helm/bitnami/common
@@ -23,4 +23,4 @@ name: stackspin-dashboard
 sources:
   - https://open.greenhost.net/stackspin/dashboard/
   - https://open.greenhost.net/stackspin/dashboard-backend/
-version: 1.8.2
+version: 1.8.3
diff --git a/deployment/helmchart/values.yaml b/deployment/helmchart/values.yaml
index 324e62aa794d2c1036ff2b595d7b979b5cc59de3..df3f412f310f9c7c2627a3c5934cdb322f315f79 100644
--- a/deployment/helmchart/values.yaml
+++ b/deployment/helmchart/values.yaml
@@ -68,7 +68,7 @@ dashboard:
   image:
     registry: open.greenhost.net:4567
     repository: stackspin/dashboard/dashboard
-    tag: 0.8.2
+    tag: 0.8.3
     digest: ""
     ## Optionally specify an array of imagePullSecrets.
     ## Secrets must be manually created in the namespace.
@@ -236,7 +236,7 @@ backend:
   image:
     registry: open.greenhost.net:4567
     repository: stackspin/dashboard/dashboard-backend
-    tag: 0.8.2
+    tag: 0.8.3
     digest: ""
     ## Optionally specify an array of imagePullSecrets.
     ## Secrets must be manually created in the namespace.
@@ -723,7 +723,7 @@ tests:
   image:
     registry: open.greenhost.net:4567
     repository: stackspin/dashboard/cypress-test
-    tag: 0.8.2
+    tag: 0.8.3
     pullPolicy: IfNotPresent
   credentials:
     user: ""