From 78aebde2ad8c57391ead55248d1f8c6d27c1bcce Mon Sep 17 00:00:00 2001 From: Alexandre de Souza Date: Tue, 25 Mar 2025 12:25:55 -0300 Subject: [PATCH] Allow user to logout from Livebook app (#2961) --- lib/livebook_web/helpers/session_helpers.ex | 28 ++++++++++++++++++++ lib/livebook_web/live/app_session_live.ex | 6 +++++ lib/livebook_web/live/hooks/app_auth_hook.ex | 22 ++++++++++++++- lib/livebook_web/live/hooks/sidebar_hook.ex | 14 ++-------- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/lib/livebook_web/helpers/session_helpers.ex b/lib/livebook_web/helpers/session_helpers.ex index c24506ab1..33f7bdc3e 100644 --- a/lib/livebook_web/helpers/session_helpers.ex +++ b/lib/livebook_web/helpers/session_helpers.ex @@ -274,4 +274,32 @@ defmodule LivebookWeb.SessionHelpers do Livebook.Session.register_file(session_pid, path, key) end end + + @logout_topic "logout" + + @doc """ + Subscribes to #{@logout_topic} topic. + """ + @spec subscribe_to_logout() :: :ok | {:error, term()} + def subscribe_to_logout do + Phoenix.PubSub.subscribe(Livebook.PubSub, @logout_topic) + end + + @doc """ + Shows the confirmation modal to logout user from Livebook. + """ + @spec confirm_logout(Socket.t()) :: Socket.t() + def confirm_logout(socket) do + on_confirm = fn socket -> + Phoenix.PubSub.broadcast(Livebook.PubSub, @logout_topic, :logout) + put_flash(socket, :info, "Livebook is logging out. You will be redirected soon.") + end + + confirm(socket, on_confirm, + title: "Log out", + description: "Are you sure you want to log out Livebook now?", + confirm_text: "Log out", + confirm_icon: "logout-box-line" + ) + end end diff --git a/lib/livebook_web/live/app_session_live.ex b/lib/livebook_web/live/app_session_live.ex index cd1ac816a..45de41e21 100644 --- a/lib/livebook_web/live/app_session_live.ex +++ b/lib/livebook_web/live/app_session_live.ex @@ -140,6 +140,12 @@ defmodule LivebookWeb.AppSessionLive do Debug + <.menu_item :if={Livebook.Config.logout_enabled?() and @current_user.email != nil}> + +
diff --git a/lib/livebook_web/live/hooks/app_auth_hook.ex b/lib/livebook_web/live/hooks/app_auth_hook.ex index 1cd5eee49..4cc863162 100644 --- a/lib/livebook_web/live/hooks/app_auth_hook.ex +++ b/lib/livebook_web/live/hooks/app_auth_hook.ex @@ -41,9 +41,17 @@ defmodule LivebookWeb.AppAuthHook do # def on_mount(:default, %{"slug" => slug}, session, socket) do + if connected?(socket) do + LivebookWeb.SessionHelpers.subscribe_to_logout() + end + livebook_authenticated? = livebook_authenticated?(session, socket) - socket = assign(socket, livebook_authenticated?: livebook_authenticated?) + socket = + socket + |> assign(livebook_authenticated?: livebook_authenticated?) + |> attach_hook(:logout, :handle_info, &handle_info/2) + |> attach_hook(:logout, :handle_event, &handle_event/3) case Livebook.Apps.fetch_settings(slug) do {:ok, %{access_type: :public} = app_settings} -> @@ -70,6 +78,18 @@ defmodule LivebookWeb.AppAuthHook do LivebookWeb.AuthPlug.authenticated?(session, uri.port) end + defp handle_info(:logout, socket) do + {:halt, redirect(socket, to: ~p"/logout")} + end + + defp handle_info(_event, socket), do: {:cont, socket} + + defp handle_event("logout", %{}, socket) do + {:halt, LivebookWeb.SessionHelpers.confirm_logout(socket)} + end + + defp handle_event(_event, _params, socket), do: {:cont, socket} + defp has_valid_token?(socket, app_settings) do connect_params = get_connect_params(socket) || %{} diff --git a/lib/livebook_web/live/hooks/sidebar_hook.ex b/lib/livebook_web/live/hooks/sidebar_hook.ex index 2480843df..e7e0d34df 100644 --- a/lib/livebook_web/live/hooks/sidebar_hook.ex +++ b/lib/livebook_web/live/hooks/sidebar_hook.ex @@ -9,6 +9,7 @@ defmodule LivebookWeb.SidebarHook do def on_mount(:default, _params, _session, socket) do if connected?(socket) do Livebook.Hubs.Broadcasts.subscribe([:crud, :connection]) + LivebookWeb.SessionHelpers.subscribe_to_logout() Phoenix.PubSub.subscribe(Livebook.PubSub, "sidebar") end @@ -67,18 +68,7 @@ defmodule LivebookWeb.SidebarHook do end defp handle_event("logout", _params, socket) do - on_confirm = fn socket -> - Phoenix.PubSub.broadcast(Livebook.PubSub, "sidebar", :logout) - put_flash(socket, :info, "Livebook is logging out. You will be redirected soon.") - end - - {:halt, - confirm(socket, on_confirm, - title: "Log out", - description: "Are you sure you want to log out Livebook now?", - confirm_text: "Log out", - confirm_icon: "logout-box-line" - )} + {:halt, LivebookWeb.SessionHelpers.confirm_logout(socket)} end defp handle_event(_event, _params, socket), do: {:cont, socket}