hourram
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.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
kartheek
@hourram Welcome to elixir community.
assign_new/3assigns only when the key does not exists in socket_or_assigns.assign/3just assigns the value every time it is called.What it means is -
assign_new/3is used so:current_userkey is not assigned every time on_mount is called. It is assigned only if:current_userkey 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.
hourram
Thanks for the answer.
Then I don’t know why my assigns are reset when changing LiveView.
For exemple, this line will always query the DB for session token because assigns are always empty.
If I put
IO.inspect(socket.assigns)at the top of my UserLiveAuth on_mount, there is no:current_useror:user_return_to.03juan
This part of the security considerations doc might clear up the confusion
And from
live_redirect/2If you are already setting the user through a plug on first page load, then
assign_newin theon_mountwill ensure that you don’t hit the database again. But when youlive_redirectto 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, andassign_newhas 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.
hourram
It’s all clear.
Thanks for the help.
ani
I don’t think the assigns from conn struct are transferred to the socket assigns right? So I think the function in
assign_newwill be run in theon_mounthook.03juan
The
conn.current_userassign is passed insocket.private.assign_newtoon_mount.My browser pipeline calls
plug :fetch_current_userfrom phx.gen.auth’sUserAuth, which doesassign(conn, :current_user, user).Try the following and look at your logs when refreshing a live page, and then when clicking a live_redirect link that goes to the same live_session on the page:
ani
Tried it now and yes, it does looks like the function isn’t run when
:current_useris set in theconn. I had tried earlier by just loggingsocket.assignsand in that,current_userwasn’t set, so I assumed the function will be run. Thanks for the clarification!03juan
Yes, I was also confused when I tried to inspect
socket.assignsfirst time, since the function doc saysI needed to see the function code to understand it forces the assign for you if the key is in
private.assign_new.