Need advice regarding basic handling of non-logged / anonymous users - public chat rooms

Greetings,

Newbie here. I’m building a simple, public chat room in which anyone can view, but only logged in users can chat.

Question 1 - User Socket Connection:

The way its set up right now, when a visitor views the chat room, the user_socket.ex script checks for a token, which will either assign a user_id to the socket, or if there is no token(non-logged, anonymous user), it will assign “nil” as the user_id. Basically, a user_id is always set upon socket connection.

Does this sound like a good way of doing it, like is this what you would do, or is there a more proper way?

Question 2 - Joining The Room:

In my room_channel.ex script, I have something set up like this:

defmodule ExchatWeb.RoomChannel do
  use Phoenix.Channel
  alias ExchatWeb.Presence

  def join("room:lobby", _message, socket) do
    send(self(), :after_join)
    {:ok, socket}
  end

  def handle_info(:after_join, socket) do
    {:ok, _} = Presence.track(socket, "user:#{socket.assigns.user_id}", %{
      online_at: inspect(System.system_time(:second)),
      user_id: socket.assigns.user_id
    })
    IO.inspect(socket)
    push(socket, "presence_state", Presence.list(socket))
    IO.inspect(Presence.list(socket))
    {:noreply, socket}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body})
    {:noreply, socket}
  end
end

Note that I have Presence available, as I want to track the number of non-logged, anonymous “viewers” who are in the room and display the count. All the “nil” users are somewhat counted. However, it’s inaccurate as it counts up the number of tabs they have open. One anonymous user could have 20 tabs open, and the “viewer” count shows up as “20”. How would you handle this?

Question 3 - Prevent non-logged message from broadcasting

I’m using the default phoenix learning guide script here to send messages:

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body})
    {:noreply, socket}
  end

Pardon my inexperience, but how do I set up the code logic so that only messages from logged in users will be broadcast? Like what’s the function to check that the socket.assigns.user_id is “not nil”?

Lastly, do you think I should be using “roles”, such as role = 0 for non-logged users and role = 1 for logged users and pattern match against “role” instead of “user_id”? Any other advice is greatly appreciated.

You can issue a unique fake user id like "visitor:#{Ecto.UUID.generate()}" to each non-logged-in user and put it in a cookie if it’s not already there. When establishing WebSocket connection, you can use the real user id (if logged in) or this fake user id (if not logged in) as the connection id.

1 Like