LiveView authentication failed: how to redirect to login page?

It is not clear for me how to handle authentication with LiveView. From some posts here it appears that login form should not be a Liveview in order to set the session variables that can be used by both Liveview and views.

In my application, the user logs in and is redirected to a Liveview. My problem is that if how do I check if the user can display that liveview? In many websites, when a user has no access to a resource, he is redirected to a login page.
I tried to do the same with my liveviews and checked session variables from the mount/2 function in the liveview but redirection is not allowed from that function. Do you have any clues on how this can be cleanly done? I could render something else in the “render” function but since the login view is not a liveview (for the reasons mentionned earlier) I can it to work this way.

I could of course add a big “if” in all of my templates and show a link to the login page if authentication fails but that looks like a ugly solution.

Any hints would be appreciated. Thanks.

Are you referring to authentication or authorization? If it’s just authentication, then you can still use plugs in your endpoint or router that check for a credential, and if it’s missing, redirect to login. If you’re trying to sort out if the currently logged in user has the rights to view some specific page, that’s an authorization concern, and I’d probably handle it in mount/2.

1 Like

Thanks for the hint. Using a custom plug to check if the user exists in the session works for authentication. I did it using a dedicated pipeline that uses my custom plug:

   pipeline :authenticated do
     plug :accepts, ["html"]
     plug :fetch_session
     plug :fetch_flash
     plug Phoenix.LiveView.Flash
     plug :protect_from_forgery
     plug :put_secure_browser_headers
     plug HelloWeb.Auth
   end

and creating scopes that use that pipeline:

  scope "/me", HelloWeb do
    pipe_through :authenticated
    live "/", MeLive, session: [:user_id, :current_user]
  end

My custom plug redirects to login page if no user is logged.

However, I still can’t figure how to do this for the liveviews in mount/2. Adding a redirect like this:

    {:ok,
      socket
      |> put_flash(:error, "Unauthorized resource")
      |> redirect(to: "/access-denied")
    }

gives the following error:

cannot redirect socket on mount/2

mount/2 doesn’t look like the place to do authorization checks with redirects

Mount only gets checked once right?

What if you had a deactivated_at field on a user and you wanted that to be checked on every request (similar to what you would do with a session based plug without LV). This would allow you to log out a user as soon as they change pages. This is a very common thing I do in non-LV style apps but would be nice to do in LV too.

I know this is an old thread, but maybe it can help me tomorrow
I found out that you need to use
{:noreply, push_redirect(socket, to: Routes.live_path(socket, _MyLV, _assign))}
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-live-navigation

4 Likes

You can also check if a user is activated before performing every action.

For example, if you’re patching the browser for important actions (like opening an Edit form) you can place a single function that reloads the user from the database and check if the user is deactivated on handle_params. By the way, if you are using handle_params you might want to remove this check in the mount (if you don’t care about the disconnected mount).

Or if you have an index page and you want to avoid actions like delete, you can place this check on handle_event(“delete”, %{ “id” => id}, socket) do… for example.

Of course, this will be slower since you will need to hit the database again to check the user each time. Alternatives to that is to use a “expiry time” to reload the user only the user has expired (each 10 minutes for example) or use pubsub to disconnect when he/she is deactivated by someone.

1 Like