hourram
A few questions on assigns and assign_new()
Hello,
I am working on a web app with Phoenix + Liveview.
I have an authenticated live_session in my router.ex
live_session :dashboard,
on_mount: [MyappWeb.RouteInfo, MyappWeb.UserLiveAuth, MyappWeb.LiveAdminSidebar] do
scope "/dashboard", MyappWeb do
pipe_through [:browser, :require_authenticated_user]
live "/", WelcomeLive.Index, :index
scope "/settings", SettingsLive do
live "/account", Account, :edit
live "/account/confirm_email/:token", Account, :confirm_email
end
scope "/users", UserLive do
live "/", Index, :index
live "/new", Index, :new
live "/:id/edit", Index, :edit
live "/:id", Show, :show
live "/:id/show/edit", Show, :edit
end
scope "/roles", RoleLive do
live "/", Index, :index
live "/new", Index, :new
live "/:id/edit", Index, :edit
live "/:id", Show, :show
live "/:id/show/edit", Show, :edit
end
end
end
I use the on_mount to handle authentication and authorization as described in this doc page: Security considerations of the LiveView model
Here is my UserLiveAuth on_mount function
def on_mount(:default, _params, %{"user_token" => user_token} = _session, socket) do
socket = socket
|> assign_new(:current_user, fn -> Accounts.get_user_by_session_token(user_token) end)
|> assign_new(:user_return_to, fn -> &Routes.welcome_index_path(&1, :index) end)
|> assign_current_user_access_for_routes()
cond do
!is_user_logged_in(socket) ->
{
:halt,
socket
|> put_flash(:error, "You must log in to access this page.")
|> redirect(to: Routes.user_session_path(socket, :new))
}
!does_user_have_required_capability_for_current_view(socket) ->
{
:halt,
socket
|> put_flash(:error, "Your current role does not allow you to access this page.")
|> push_redirect(to: socket.assigns.user_return_to.(socket))
}
true ->
{
:cont,
assign(socket, :user_return_to, socket.assigns.current_route_info.menu_item.path)
}
end
end
Here is what I’m trying to achieve.
When the authenticated user tries to go to a page he is not allowed to visit (2nd cond), I want to redirect him to the :user_return_to which would be the last valid page he was on.
So I tried to store the current page on the :cont case (3rd cond), but I realised that the assigns are not persisted after a live_redirect.
Is it a normal behaviour?
If so, I am not sure of the purpose of assign_new over assign.
Here is what is explained in the security considerations of the LiveView model
[assign_new] is a convenience to avoid fetching the
current_usermultiple times across LiveViews
In my case current_user is fetched everytime I navigate to a new LiveView of the same live session (using live_redirect links).
Is assign_new only useful when the socket connection is established?
How could I do to store the :user_return_to across different LiveViews? Use localStorage?
Thanks in advance for your help.
Marked As Solved
03juan
This part of the security considerations doc might clear up the confusion
If you perform user authentication and confirmation on every HTTP request via Plugs, such as this:
plug :ensure_user_authenticated plug :ensure_user_confirmed
And from live_redirect/2
The current LiveView will be shut down and a new one will be mounted in its place, without reloading the whole page. This can also be used to remount the same LiveView, in case you want to start fresh. If you want to navigate to the same LiveView without remounting it, use
live_patch/2instead.
If you are already setting the user through a plug on first page load, then assign_new in the on_mount will ensure that you don’t hit the database again. But when you live_redirect to another live view, the framework kills the previous live view’s process and starts a new one without hitting the plug. In that case the assign will be empty, and assign_new has to fetch the user again because the plug isn’t being run.
I think this part of the security consideration doc is misleading and probably needs clarification.
We use
assign_new/3. This is a convenience to avoid fetching thecurrent_usermultiple times across LiveViews.
Also Liked
kartheek
@hourram Welcome to elixir community.
assign_new(socket_or_assigns, key, fun)
Assigns the givenkeywith value fromfunintosocket_or_assignsif one does not yet exist.
assign_new/3 assigns only when the key does not exists in socket_or_assigns.
assign/3 just assigns the value every time it is called.
What it means is - assign_new/3 is used so :current_user key is not assigned every time on_mount is called. It is assigned only if :current_user key does not exist in socket.
ani
assign_new won’t run the function if the key already exists. So, for example, if you need to fetch the current user from the DB, if you use assign_new it will query the DB only once and store the value. Even when different live views are mounted it will not query the DB.
ani
Tried it now and yes, it does looks like the function isn’t run when :current_user is set in the conn. I had tried earlier by just logging socket.assigns and in that, current_user wasn’t set, so I assumed the function will be run. Thanks for the clarification!
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









