From ddaf5123c359428ed3a7fb94d498017a67b488e7 Mon Sep 17 00:00:00 2001
From: Arie Peterson <arie@greenhost.nl>
Date: Thu, 19 Oct 2023 11:58:06 +0200
Subject: [PATCH] Retry failed logout attempts from the start in some cases

---
 backend/web/login/login.py | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/backend/web/login/login.py b/backend/web/login/login.py
index 177bdd90..43c521c7 100644
--- a/backend/web/login/login.py
+++ b/backend/web/login/login.py
@@ -570,28 +570,41 @@ def logout():
     Returns:
         Redirect to the url that is provided by the LogoutRequest object.
     """
+    # Generally, if we encounter errors during these steps we redirect the user
+    # to the beginning of the logout procedure, which is
+    # `https://${hydra_domain}/oauth2/sessions/logout`.
+    new_logout_url = "https://{}/oauth2/sessions/logout".format(HYDRA_PUBLIC_URL)
+
+    # We should have been redirected here by hydra which also sets the
+    # `logout_challenge` parameter.
     challenge = request.args.get("logout_challenge")
     current_app.logger.info("Logout request: challenge=%s", challenge)
     if not challenge:
-        abort(403)
+        current_app.logger.info("No challenge set.")
+        current_app.logger.info("Redirecting to hydra logout: %s", new_logout_url)
+        return redirect(new_logout_url)
     try:
         # hydra v2
         # logout_request = oauth2_api.get_o_auth2_logout_request(challenge)
         logout_request = hydra_admin_api.get_logout_request(challenge)
     except hydra_exceptions.NotFoundException:
         current_app.logger.error("Logout request with challenge '%s' not found", challenge)
-        abort(404, "Hydra session invalid or not found")
+        return redirect(new_logout_url)
     except hydra_exceptions.ApiException:
         current_app.logger.error(
             "Conflict. Logout request with challenge '%s' has been used already.",
             challenge)
-        abort(404, "Logout request has been accepted already.")
+        current_app.logger.info("Redirecting to hydra logout: %s", new_logout_url)
+        return redirect(new_logout_url)
 
     current_app.logger.info("Logout request hydra, subject %s", logout_request.subject)
 
-    # Accept logout request. We ignore the redirect URL
-    # (`hydra_return.redirect_to`) because we also need to do the kratos logout
-    # browser flow and we can't do both.
+    # Accept logout request. The `redirect_to` that we get is what we have to
+    # redirect the browser to to finish the hydra logout (clear cookies, etc.)
+    # and after that get redirected to the configured post-logout URL. We store
+    # the `redirect_to` URL so we can pass it to kratos below (as `return_to`),
+    # so the browser will get redirected to `redirect_to` after the kratos
+    # logout is finished.
     try:
         hydra_return = hydra_admin_api.accept_logout_request(challenge)
         next_redirect = hydra_return.redirect_to
@@ -603,7 +616,7 @@ def logout():
     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");
+        current_app.logger.info("Expected kratos cookie but not found. Skipping kratos logout but continuing other logout steps.");
         # We skip the Kratos logout, but we still need to follow
         # `next_redirect` -- probably the Hydra logout URL -- and clear
         # dashboard storage.
@@ -623,7 +636,8 @@ def logout():
         current_app.logger.error("Exception when calling"
             " create_browser_logout_flow: %s\n",
             ex)
-        return redirect(DASHBOARD_URL)
+        current_app.logger.info("Redirecting to hydra logout: %s", new_logout_url)
+        return redirect(new_logout_url)
 
 
 if DEMO_INSTANCE:
-- 
GitLab