Phx.gen.auth and liveview modal form component with no path

I have a modal form_component (generated by phx.gen.live). The liveview that kicks off the form_component is available to all users. But I want that form_component to require authentication via phx.gen.auth functionality. The problem is that the form_component doesn’t have a route that I can put under pipe_through [:browser, :require_authenticated_user]

Use Case is very similar to Pinterest: User is searching resources that anyone can view. They click on a link to “Save” the resource to a personal library of resources. User can have multiple libraries. Once they click “Save”, I want the user to be presented with a form_component that just asks them to select what library they want to save the resource to. But that requires authentication for only the form_component not the parent Liveview.

Possible Solution: I use a handle_event for “Save” to check for authentication and force user to authenticate if not logged in. Once authenticated, I kick off the form_component. Is that the best way to do this? Can I force authentication from inside the LiveView? I normally just doing it via the routes.

Check the authentication at the LiveView stage and rather than rendering the modal form, render the modal signup | login form.

How do I get the user back to the modal form once authenticated? If I set the “socket.assigns.return_to” the modal path, won’t that socket get lost/cleared once the user is routed to the login form?

You can send to the login route with an explicit return url return=/home/stuff?modal=true

after authenticating, if the return is set then redirect to that url. Make sure your return is a whitelist!

if return and return in whitelisted_urls do

then in your LiveView, if params["modal"] then popup your modal.

Sorry … I’m still new at all of this stuff. What is a whitelist? Do you mean that the return liveview must be in the “authenticated” section of routes? Unfortunately, I want them to return to the list of resources that must be in the “unauthenticated” route section because it is originally available to view by any user.

no, as in place the user is allowed to return to

whitelist = ['/home/stuff', '/account']

I have not been implementing any whitelists for the users. I have just been using router.ex to enforce authentication on the liveviews that require an authenticated user. Am I supposed to also be implementing whitelists?

I have provided an example up there. literally list of strings that application is allowed to redirect to. That is all

It is for security reasons so if someone sends a url such as return=...porndomain.... your users don’t end up on a porn or scam website.

I believe I ran into exactly this lately, juggling Phx.gen.auth and liveview. Phx.gen.auth generates code that adds user_token to the session info (see UserAuth.log_in_user/3 - the line put_session(:user_token, token)). This is a cookie that is set on the client and returned with each request.

But in order to access session info inside liveview socket assigns, you need to extract the data from the session and assign it in a LiveView’s mount.

I’ll try to provide a very minimal code sample:

defmodule MyAppWeb.RoomLive.Index do
  use MyAppWeb, :live_view

  def mount(_params, session, socket) do
    socket = assign_user(session, socket)

    {:ok, %User{} = user} <- get_current_user(socket)

    #...
  end

   defp assign_user(%{"user_token" => user_token}, socket) do
    Phoenix.LiveView.assign_new(socket, :current_user, fn ->
      Accounts.get_user_by_session_token(user_token)
    end)
  end

  defp assign_user(_session, socket) do
    socket
  end

  defp get_current_user(socket) do
    case Map.get(socket.assigns, :current_user) do
      nil -> {:error, :not_authenticated}
      user -> {:ok, user}
    end
  end
end

You need to do this in a LiveView and pass it down to the respective LiveComponent.

Apologies if I’m misreading your use-case, please let me know if so.

1 Like

Ahh… hadn’t thought about someone trying to redirect users to porn site. Definitely don’t want that.

I’m running into two issues:

  1. All of the routes related to user authentication (user_registration_path, user_session_path, sketcher_confirmation_path, etc) require a conn as a parameter. But I’m sitting in a liveview and just have a session and a socket.
  2. Where do I put the whitelist return code that you suggested? Do I put that in the log_in_user(conn, user, params) function within the user_auth.ex controller? I see that the log_in_user function is grabbing the :user_return_to set inside the session. What you’re suggesting is that I unpack the params and reset the return_to path (after checking the whitelist). Is that correct?

Thanks for your post. I’m trying to do something slightly different. I’ve got code that will allow me to access the user within liveviews that are under authenticated paths. But my use case requires a liveview that can’t require authentication, but I want it to kick off a form modal that does require authentication. Unfortunately, form modals don’t have paths that get listed in routers.ex. So I’ve got to redirect to a user authentication path for authentication only when the user clicks “Save” … but I don’t have a conn.