polypush135
Example of best way to hydrate sessions for LiveView
Going off the example auth blog post where we have a plug helper like so.
# lib/my_app/router.ex
import MyAppWeb.UserAuth
pipeline :browser do
...
plug :fetch_current_user
end
# lib/my_app_web/controllers/user_auth.ex
@doc """
Authenticates the user by looking into the session
and remember me token.
"""
def fetch_current_user(conn, _opts) do
{user_token, conn} = ensure_user_token(conn)
user = user_token && Accounts.get_user_by_session_token(user_token)
assign(conn, :current_user, user)
end
In the context of a LiveView module, I see the user_token in the session on the mount callback.
I have seen the notes about sessions being serialized into strings, should I be concerned about upstream pipelines that adds this usrer_token to the session? When inspecting my session values on the mount I see a binary for the user token.
"user_token" => <<..,, ..., ..., ..., >>
Also, I’ve seen the notes about storing the user id vs the whole user value because of the string serialization.
After some reading, it looks like the past way of setting up the session from the route no longer works. Remove deprecate features · phoenixframework/phoenix_live_view@0020c35 · GitHub
My other question is do I just make another plug that sets the session with the user_id for the socket?
if so I assume I then will need to query for the user via the user id every request for LiveView and auth. Is there a better way?
Marked As Solved
chrismccord
your second greedy match mount/3 should not be necessary and the fact that you are getting there is definitely the issue. The connected call will include the Plug session , provided you have wired up the session options in your endpoint and csrf token in app.js. Can you post your endpoint? If you are getting here:
# Runs the second time for the js call
def mount(params, session, socket) do
# Does not show user_token or current_user
IO.inspect([params, session, socket])
{:ok, socket}
end
Then it makes sense the current_user is not there, because it was never set. The first clause should have matched, so let’s kill the catch-all and figure out why your plug session isn’t being provided.
Also Liked
chrismccord
You need to pull the user_token from the session and fetch the user in the same way the plug pipeline does. You can make use of assign_new to avoid fetching on the HTTP request, but you need to fallback to fetching on the connected mount, using the same mechanism that the plug pipeline does:
def mount(_params, %{"user_token" => user_token}, socket) do
{:ok,
socket
|> ...
|> assign_new(:current_user, fn ->
Accounts.get_user_by_session_token(user_token)
end)}
end
To avoid duplication in your LVs, you could pull this out into an assign_defaults function that you write to wire up common assigns, like current_user. Make sense?
ChristopheBelpaire
Hello,
I wanted to achieve the same kind of behaviour and be able to access to the current_user in my live view assigns.
The best would be able to assign the current_user to the socket assign, but it doesn’t seems to be possible.
The only way I found to achieve this is to inject a mount function, in live :
def live do
quote do
use BrandManagerWeb.LoadUser
use Phoenix.LiveView, layout: {BrandManagerWeb.LayoutView, "app.html"}
alias BrandManagerWeb.Router.Helpers, as: Routes
end
end
defmodule BrandManagerWeb.LoadUser do
alias Core.Accounts
alias Phoenix.LiveView
defmacro __using__(_opts) do
quote do
def mount(params, %{"user_id" => user_id} = session, socket) do
session = Map.delete(session, "user_id")
socket =
LiveView.assign_new(socket, :current_user, fn ->
manager_id && Accounts.get_user(user_id)
end)
mount(params, session, socket)
end
end
end
end
It is working, but it is kind of a hack ![]()
Is there a better way to achieve this ?
Thanks in advance!
sfusato
That’s now the default behaviour (all the session is passed and you don’t need to specify the keys):
When a LiveView is rendered, all of the data currently stored in the connection session (see
Plug.Conn.get_session/1) will be given to the LiveView.It is also possible to pass additional session information to the LiveView through a session parameter:
# In the router live "/thermostat", ThermostatLive, session: %{"extra_token" => "foo"} # In a view <%= live_render(@conn, AppWeb.ThermostatLive, session: %{"extra_token" => "foo"}) %>Notice the
:sessionuses string keys as a reminder that session data is serialized and sent to the client. So you should always keep the data in the session to a minimum. I.e. instead of storing a User struct, you should store the “user_id” and load the User when the LiveView mounts.
You could always add a cache for getting the user with a low refresh ttl value.
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









