Cross site Liveview Endless Redirect

On localhost from 4030 to 4010. Traffic is sent to 4010 and accepted, but then for some reason it tries to subscribe to

|["4","4","lv:phx-GAUwaqEigomHsjrB","phx_join",{"url":"http://localhost:4030/","params":{"_csrf_token":"JC9nCxkWTi4eCH4uMQ0LGl5tYQI5BR0aeY7zt_6AYX9FpCa-l9SVvKHv","_track_static":["http://localhost:4030/assets/app.css","http://localhost:4030/assets/app.js"],"_mounts":0},"session":"SFMyNTY.....{redacted}"}]|812|15:41:06.380|
| --- | --- | --- |
|["4","4","lv:phx-GAUwaqEigomHsjrB","phx_reply",{"status":"error","response":{"reason":"stale"}}]|96|15:41:06.381|
|["4","8","lv:phx-GAUwaqEigomHsjrB","phx_leave",{}]|50|15:41:06.382|
|[null,"9","lv:","phx_leave",{}]|31|15:41:06.382|
|["4","8","lv:phx-GAUwaqEigomHsjrB","phx_reply",{"status":"ok","response":{}}]|77|15:41:06.383|
|[null,"9","lv:","phx_reply",{"status":"ok","response":{}}]|

How do I correct this?

let liveSocket = new LiveSocket("ws://localhost:4010/live", Socket, {
  params: params,
  hooks: Hooks
});

What code is in place to redirect?

There isnt any. But as soon as I do not connect to /live the problem is resolved. It appears, though I cannot identify the cause, its something inside of the liveview lib JS. It acts as if Im not passing in the csrf token but does not produce the debug logs when coming from a diff domain.

What I mean is when I do not pass in the token:

  • and use 4010 it endlessly redirects and produces a debug log saying “you need a token”.
  • and use 4030 it endlessly redirects and does not produce debug logs

Im getting this message from 4010

22:01:57.629 [info] CONNECTED TO Phoenix.LiveView.Socket in 35µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4030/assets/app.css", "1" => "http://localhost:4030/assets/app.js"}, "vsn" => "2.0.0"}

then from 4030 in mount/3 I get

22:01:57.653: {:ok,
 #Phoenix.LiveView.Socket<
   id: "phx-GAWAIougoPgQL1fB",
   endpoint: ClientWeb.Endpoint,
   view: Session.Index,
   parent_pid: nil,
   root_pid: nil,
   router: ClientWeb.Router,
   assigns: %{
     __changed__: %{page_name: true, waiting_text: true},
     page_name: "index",
     waiting_text: "Waiting to connect...",
     flash: %{},
     live_action: nil
   },
   transport_pid: nil,
   ...
 >}

So it first connects to 4010 then tries to connect to 4030 which brings me back the OP.

The URL in the request and the files being tracked are for 4030 despite sending the request to 4010

|["4","4","lv:phx-GAUwaqEigomHsjrB","phx_join",{"url":"http://localhost:4030/","params":{"_csrf_token":"JC9nCxkWTi4eCH4uMQ0LGl5tYQI5BR0aeY7zt_6AYX9FpCa-l9SVvKHv","_track_static":["http://localhost:4030/assets/app.css","http://localhost:4030/assets/app.js"],"_mounts":0},"session":"SFMyNTY.....{redacted}"}]|812|15:41:06.380|

What am I not understanding here?

2 fresh out of the box installs

localhost:4000

[info] CONNECTED TO Phoenix.LiveView.Socket in 33µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "F3ceeiE5ERNVLjh8AH4dVCFsKgQpPC19o5W1xHCb8bA7sLGaUALWmlFN", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4010/assets/app.css", "1" => "http://localhost:4010/assets/app.js"}, "vsn" => "2.0.0"}
[info] CONNECTED TO Phoenix.LiveView.Socket in 29µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "AA88GCobJyUEFjM8PVYvARsaJQI3CBJhxMuSsjuTiZJwNdu4o7CQsXyR", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4010/assets/app.css", "1" => "http://localhost:4010/assets/app.js"}, "vsn" => "2.0.0"}
[info] CONNECTED TO Phoenix.LiveView.Socket in 29µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "DAs7GjgYKwFdHBovAkotQSwZCSkxPgF5tIrQaiyp0PcdqxwtX4ozunjJ", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4010/assets/app.css", "1" => "http://localhost:4010/assets/app.js"}, "vsn" => "2.0.0"}

localhost:4010

[info] Sent 200 in 844µs
[info] GET /
%{"_csrf_token" => "xBIKYqRqmLyKs2Z5t-fSDPk3"}
[debug] Processing with Session.Index.Elixir.Session.Index/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 200 in 1ms
[info] GET /
[debug] Processing with Session.Index.Elixir.Session.Index/2
  Parameters: %{}
  Pipelines: [:browser]
%{"_csrf_token" => "xBIKYqRqmLyKs2Z5t-fSDPk3"}
[info] Sent 200 in 789µs
[info] GET /
[debug] Processing with Session.Index.Elixir.Session.Index/2
  Parameters: %{}
  Pipelines: [:browser]
%{"_csrf_token" => "xBIKYqRqmLyKs2Z5t-fSDPk3"}
[info] Sent 200 in 982µs

On both

# app.js
let liveSocket = new LiveSocket("ws://localhost:4000/live", Socket)
# endpoint.ex
socket "/live", Phoenix.LiveView.Socket
# router.ex
live_session :index do
  live("/", Index)
end
# module / scope used in route
defmodule Session.Index do
  use OneWeb, :live_view
  require Logger

  @impl true
  def mount(_params, session, socket) do
    session |> IO.inspect()

    {:ok, socket}
  end
end
# dev.ex
config :app, AppWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
  http: [ip: {127, 0, 0, 1}, port: 4000],
  check_origin: false,
  code_reloader: false,
  debug_errors: true,
  secret_key_base: "SOME_STRING",
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
  ]

with debug enabled in app.js

phx-GAXBNEG4QrFB-imB error: unauthorized live_redirect. Falling back to page request -  {reason: 'stale'}
utils.js:28 phx-GAXBNEG4QrFB-imB socket: disconnect for page nav -  undefined
utils.js:28 phx-GAXBNEG4QrFB-imB destroyed: the child has been removed from the parent -  undefined
utils.js:28  destroyed: the child has been removed from the parent - 

any ideas given the above?

Its because the secret key base and signing salt are rand generated… I would like this to remain the case… how do I do so?

I can not replicate the issue. Have 2 fresh installs of Phoenix running, one on 4010 and one on 4034 and both stay “in their own scope”.

If the CSRF token is an issue, have a look at this thread: HTTP-caching LiveView's first render. Then you need to rely on origin.