I am a beginner in Elixir/Phoenix/LiveView and have finished a few courses + exercises in the last few weeks. I am now trying to write a simple app to implement the concepts, but am stuck when it comes to scoping a resource to a logged-in user.
The app should allow users to log in and manage their own tasks. These are the commands I used to create the app:
mix phx.new test_app
cd test_app
mix ecto.create
mix phx.gen.auth Accounts User users
mix deps.get
mix phx.gen.live Tasks Task tasks name completed:boolean user_id:references:users
mix ecto.migrate
I have been able to scope the :index functionality to the user by going through the following steps:
- Created an
assign_current_user
helper underlib/test_app/web/live/live_helpers.ex
:
def assign_current_user(socket, session) do
assign_new(
socket,
:current_user,
fn ->
Accounts.get_user_by_session_token(session["user_token"])
end
)
end
- Added the helper to the TestAppWeb entry point (
lib/test_app_web.ex
) by adding the following line in thelive_view
function:
import TestAppWeb.LiveHelpers
- Assigned the current user to the socket by modifying the
mount
function inlib/test_app_web/live/task_live/index.ex
like this:
def mount(_params, session, socket) do
socket = assign_current_user(socket, session)
{:ok, assign(socket, :tasks, list_tasks(socket.assigns.current_user.id))}
end
- Created a
list_tasks/1
function scoped to the user_id inlib/test_app/tasks.ex
:
def list_tasks(user_id) do
query = from(t in Task, where: t.user_id == ^user_id)
Repo.all(query)
end
Now when I visit localhost:4000/tasks
, I only see the tasks for the currently logged in user.
However, I have been unable to do this when it comes to creating, editing and viewing a specific task.
The first approach I tried was to add the user_id as a hidden field in lib/test_app_web/live/task_live/form_component.html.heex
. However, that would allow the user to edit the value, whereas this scoping functionality should ideally be performed on the server. Furthermore, the user information is not accessible on the socket anymore when form_component.ex
is evaluated, and I was unable to figure out how to add it.
The second approach I tried was to go through the Ecto guide for Multi tenancy with foreign keys, but using user_id instead of org_id. This resulted in various errors for me, so I concluded that for now I would just do it manually for the /tasks CRUD operations and worry about doing this globally later on.
I now think that the best approach would be to modify the TestApp.Tasks module so it adds the user_id before performing the rest of the CRUD operations. However, most of these are called from lib/test_app_web/live/task_live/form_component.ex
, which doesn’t have a mount() function to add the current user to the socket, so I am unable to figure out how to do this.
Could someone here advise me on the best way to achieve this? I.e., securely scope the tasks to the currently logged-in user, so each user can only view, create, edit and delete their own tasks?
Here is a repo with the full application code for reference: https://github.com/bysja/test-app