Scenario:
User connects to socket, which has multiple channels. Followed by an authentication check with a token in connect/3
. On passing the check, the user and roles of the user are fetched and stored in assigns
.
defmodule MyAppWeb.UserSocket do
use Phoenix.Socket
channel "chat_room:*", MyAppWeb.ChatRoomChannel
channel "role:*", MyAppWeb.RoleChannel
channel "user:*", MyAppWeb.UserChannel
...
def connect(%{"token" => token}, socket, _connect_info) do
...
user = Repo.get_by(User, id: id) |> Repo.preload([:roles])
{:ok, assign(socket, :current_user, user)}
end
end
This state (in assigns) from the socket is copied to the channel when the respective join/3
is called. This includes the roles which are used for authorization in authorized?/2
. (Authorization is done by checking roles of the user Eg: Admin chat room should only be accessible to user with admin role)
defmodule MyAppWeb.ChatRoomChannel do
use MyAppWeb, :channel
def join("chat_room:" <> room_id, _payload, socket) do
room = Chat.get_room!(room_id)
if authorized?(socket.assigns.current_user, room)
# join channel
else
# throw error to client
end
end
end
Problem:
Suppose, we have a Channel/Controller where admins can associate/dissociate users with roles.
In this case, the channel state (assigns) will be outdated for users who were newly associated/dissociated with a role. The state of all the remaining channels associated to that user’s socket is outdated. How would you recommend they be synced so the state of all channels the user joined be updated?
(Easy fix is to query the roles
table on every join or channel event. But I would like to avoid that since changes with roles are not frequent events, so they do not need to be queried every time. Using ETS to cache the role and user data from the DB might be a good option here?)