In the Pow GitHub issue 271, Chris McCord suggests broadcasting disconnect on logout or session expiry in order to disconnect active user connections:
Note that by default since a few versions ago that you can broadcast “disconnect” to the LV socket id to invalidate/kill any active user connections on logout, as long as you add the :live_socket_id
to the session on login:
phoenix_live_view/lib/phoenix_live_view.ex at b49e828a15a0121d5cced8691089cadc122eb293 · phoenixframework/phoenix_live_view · GitHub
How could I hook into Pow to do that? Is there a way to get called back when the user logs out or the session expires? Is that what ControllerCallbacks are for?
Here is how I ended up solving it:
defmodule MyAppWeb.Pow.ControllerCallbacks do
alias Pow.Extension.Phoenix.ControllerCallbacks
alias Plug.Conn
@live_socket_id_key :live_socket_id
def before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, config) do
user = conn.assigns.current_user
conn =
conn
|> Conn.put_session(:current_user_id, user.id)
|> Conn.put_session(@live_socket_id_key, "users_sockets:#{user.id}")
ControllerCallbacks.before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, config)
end
def before_respond(Pow.Phoenix.SessionController, :delete, {:ok, conn}, config) do
live_socket_id = Conn.get_session(conn, @live_socket_id_key)
BooklistiWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
ControllerCallbacks.before_respond(Pow.Phoenix.SessionController, :delete, {:ok, conn}, config)
end
defdelegate before_respond(controller, action, results, config), to: ControllerCallbacks
defdelegate before_process(controller, action, results, config), to: ControllerCallbacks
end
Then, update config to point to the custom ControllerCallbacks:
config :myapp_web, :pow,
...
controller_callbacks: MyAppWeb.Pow.ControllerCallbacks
2 Likes
You shouldn’t really use controller callbacks for this, controller callbacks are meant for extensions.
I would recommend you to just override the delete function like this
In your router
defmodule MyAppWeb.Router do
...
scope "/" do
pipe_through [:browser]
scope "/", MyAppWeb, as: "pow" do
delete "/session", SessionController, :delete
end
pow_session_routes()
pow_extension_routes()
end
end
and create a session controller with a delete function
defmodule MyAppWeb.SessionController do
@moduledoc """
Session controller based on https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/controllers/session_controller.ex
"""
import Pow.Phoenix.Controller, only: [require_authenticated: 2]
use MyAppWeb, :controller
alias Plug.Conn
plug :require_authenticated when action in [:delete]
@spec delete(Conn.t(), map()) :: Conn.t()
def delete(conn, _params) do
MyAppWeb.Endpoint.broadcast(get_session(conn, :live_socket_id), "disconnect", %{})
conn
|> Pow.Plug.delete()
|> delete_session(:live_socket_id)
|> redirect(to: Routes.pow_session_path(conn, :new))
end
end
1 Like
Thanks, @Schultzer, appreciate it!
@Schultzer Thanks for sharing this. I am at the exact point same point, and it solved the delete part for me. Can you share an idea of how a create method has to look like? I am using the extensions PowResetPassword, PowEmailConfirmation and PowPersistentSession in my project.
Best regards
Oliver