Pattern for sending and responding to arbitrary events with connected clients

Hello there, I built a sign in form on my landing page with phoenix liveview. The sign in form is a passwordless sign in, meaning the flow is:

  1. The user fills out email and clicks submit
  2. Navigates away to their email client
  3. Clicks on a link in their email which takes them to a new tab back to my site where they are now logged in.

What I would like is that just after step three, when they open a new tab on my site, the previously open tab (which they filled out sign in form on) can be notified that the user now has a session and redirect to the logged in page.

The sign in page is using liveview, but the page I was going to redirect them to after they log in is not currently using liveview.

I was hoping there is an idiomatic pattern(s) to have a connected client be notified a new session exists and perform some action (in this case a redirect). Does the pub/sub module help here? Any recommended approaches?

Pubsub is the easiest way, without any concern for security here’s something I put up in a few minutes:

From a brand new mix phx.new my_app --no-ecto

my_app_web/live/login_live.ex

defmodule MyAppWeb.LoginLive do
  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    # Just lazy to do it correctly
    uuid = nil
    if connected?(socket), do: send(self(), :update)
    {:ok, socket |> assign(:uuid, uuid) |> assign(:should_redirect?, "false")}
  end

  def handle_info(:update, socket) do
    # Do a proper uuid, not this
    uuid = Enum.random(?a..?z)

    # You can subscribe when you submit the form and generate
    # the uuid for the email
    Phoenix.PubSub.subscribe(MyApp.PubSub, "login:#{uuid}")
    {:noreply, assign(socket, :uuid, uuid)}
  end

  # Here you receive the broadcasted message and redirect your user
  def handle_info(:redirect, socket) do
    {:noreply, socket |> assign(should_redirect?: true)}
  end

  def render(assigns) do
    ~H"""
    <div>
      <div>Should redirect: <%= @should_redirect? %></div>
      <div>Url to "login the user":</div>
      <a href={Routes.page_path(MyAppWeb.Endpoint, :index, uuid: @uuid)} target="_blank" >
        <%= Routes.page_path(MyAppWeb.Endpoint, :index, uuid: @uuid) %>
      </a>
    </div>
    """
  end
end

my_app_web/controllers/page_controller.ex

defmodule MyAppWeb.PageController do
  use MyAppWeb, :controller

  def index(conn, params) do
    uuid = params["uuid"]

    # Broadcasting to the login
    Phoenix.PubSub.broadcast(MyApp.PubSub, "login:#{uuid}", :redirect)
    render(assign(conn, :uuid, uuid), "index.html")
  end
end

In the router.ex:

  scope "/", MyAppWeb do
    pipe_through :browser

    get "/", PageController, :index
    live "/login", LoginLive
  end

Just go to /login and it should work.