Authorization on LiveView `mount` or router `pipe_through`

If I have this thing where :authorize_url_path makes sure the current user has authz

  scope "/", WBWeb do
    pipe_through([:authorize_url_path])
    live("/vault/:vault", LiveView.Module, :action)
  end

can I trust the url path when mounting the live view?

my logic is: the server wont allow the live view to mount if the pipe_through does not accept authorize.

Kinda confusing because the docs say to do the authorization twice:

However, if you perform user authentication and confirmation exclusively on HTTP requests via Plugs, such as this: [...]
Then the mount/3 callback of your LiveView should execute those same verifications:

ref: Security considerations — Phoenix LiveView v0.20.0

Edit: I already have an answer but I will post it here for posterity

This is the answer I got from a colleague:

the docs tell you this because LiveView is really two different things: 1.) the initial HTTP request is a normal browser request and needs to be authenticated using plug just like a standard phoenix app, then 2.) the websocket connects later and needs to be authenticated using on_mount callbacks. If you don’t do both, then a user could bypass authentication by initially going to an authenticated route using HTTP, and then using live navigation they could navigate to a live view they’re not suppsoed to have access to (because live navigation doesn’t perform an HTTP request, therefore doesn’t go through plug)

So live_navigation seems to “skip” the pipe_through and this is why it is important to authorize resources in liveview mounting.

There is this bit from the docs that seems good to have in mind:

It is only possible to navigate between LiveViews declared under the same router Phoenix.LiveView.Router.live_session/3. Otherwise, a full browser redirect is used.

ref: Phoenix.Component — Phoenix LiveView v0.20.0

Either way, it seems good practice to authorize resources in the liveview even if the plug will take care of it due to a full page reload, to avoid mistakes.

You should be using live_session with an on_mount hook to ensure the user is authenticated.

The following is what mix phx.gen.auth adds to myapp_web/router.ex , and is worth running in a fresh project to understand all of the code it generates:

scope "/", MyAppWeb do
    pipe_through [:browser, :redirect_if_user_is_authenticated]

    live_session :redirect_if_user_is_authenticated,
      on_mount: [{MyAppWeb.UserAuth, :redirect_if_user_is_authenticated}] do
      live "/users/register", UserRegistrationLive, :new
      live "/users/log_in", UserLoginLive, :new
      live "/users/reset_password", UserForgotPasswordLive, :new
      live "/users/reset_password/:token", UserResetPasswordLive, :edit
    end

    post "/users/log_in", UserSessionController, :create
  end

  scope "/", MyAppWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{MyAppWeb.UserAuth, :ensure_authenticated}] do
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
    end

  scope "/", MyAppWeb do
    pipe_through [:browser]

    delete "/users/log_out", UserSessionController, :delete

    live_session :current_user,
      on_mount: [{MyAppWeb.UserAuth, :mount_current_user}] do
      live "/users/confirm/:token", UserConfirmationLive, :edit
      live "/users/confirm", UserConfirmationInstructionsLive, :new
    end
  end
end

The relevant hooks used above are in MyAppWeb.UserAuth, generated in myapp_web/user_auth.ex

A key step when signing out a user is broadcasting the disconnect event to ensure all websocket channels on all nodes are also disconnected, which is already done for you in myapp_web/user_auth.ex.

Getting authentication right an handling all edge cases is hard, IMO its better start with a good foundation and customise from there.