RobertoSchneiders
User authentication across multiple tabs with LiveView
I’ve been reading about session management on Phoenix LiveView for the last couple of days and can’t seem to find a solution for this problem.
My app is basically entirely built with live views, almost all redirects are live redirects. Most views can be accessed whether you are logged in or not, similar to an e-commerce app. I have a basic authentication system generated by the mix phx.gen.auth. The login is a post HTTP request (so we can set the session cookie).
When a user logs in, ideally, the other tabs that are opened in the same browser would notice that and act accordingly. Another option would be to update the session state of those tabs once the user does an action, e.g. click on a link and navigate to another view. The problem is that everything is a live redirect so the other tabs don’t update the session state unless the user refreshes the page, which is not a great user experience.
Does anyone have any idea on how to solve this?
I’m wondering if it would be possible to force a full page reload on the other tabs when logging in.
Most Liked
thomas.fortes
Ok, did a proof of concept.
Global hook at app.html.heex
<main id="main" phx-hook="MainHook" class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl">
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
</main>
The hook
Hooks.MainHook = {
mounted() {
let token = localStorage.getItem("uuid")
if(token == null) {
// here you should push an event to the server and fetch a
// tamper proof token and save it to localStorage and
// all other security considerations
token = "1234"
}
this.pushEvent("subscribe_to_channel", {uuid: token})
}
}
The module attaching the server hooks
defmodule ExampleWeb.MultipleTabs do
import Phoenix.LiveView
def on_mount(:default, _params, _session, socket) do
{:cont,
socket
|> attach_hook(
"subscribe_to_channel",
:handle_event,
&subscribe_to_channel/3
)
|> attach_hook(:handle_reload, :handle_info, &reload_page/2)}
end
defp reload_page(_msg, socket) do
# Here you'll need to figure out a way to get the url
# to redirect to the correct place
{:cont, socket |> redirect(to: "/")}
end
defp subscribe_to_channel("subscribe_to_channel", %{"uuid" => uuid}, socket) do
# Here you should validate the token before subscribing
Phoenix.PubSub.subscribe(Example.PubSub, "reload##{uuid}")
{:cont, socket}
end
end
And then just put it in a live session in the router
live_session :default, on_mount: ExampleWeb.MultipleTabs do
live "/", HomeLive
end
Then you can call Phoenix.PubSub.broadcast(Example.PubSub, "reload#1234", []) from anywhere and all pages will redirect to /.
Security considerations aside it is pretty simple.
shamanime
You’ve implemented phx.gen.auth so you’re probably aware of the LiveView disconnect when the user logs out. That forces the tabs to reload.
If you come up with a way to identify all the tabs which belongs to the same guest user (ip?, browser fingerprint?, setting an identifier in the session at first page load and using it afterwards [like a fake user id]?) and set the live_socket_id accordingly, you can also disconnect all the “guest” LiveViews to force a page reload that will fetch the newly signed in user.
thomas.fortes
My naive first approach would be something like:
-
At any new tab check if there’s a token (preferably tamper proof) in local storage and send it to the server, if not, ask for one from the server with an unique identifiable payload (maybe an UUID) and save it in the local storage, in this case only the first page load will actually create a token.
-
Using on_mount use
attach_hook/4to handle the pushed event with the uuid to subscribe to a “guest#{uuid}” channel and the incominghandle_info/2messages from pubsub. -
When the user successfully log in you can broadcast a message to the
guest#{uuid}channel that will be handled by thehandle_info/2callback of all liveviews subscribed to that channel.
Security considerations aside (short lived tokens, single use, yadda, yadda, yadda) I think someone could build it in a couple hours.
A bit more javascript than I like to write though…
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








