Yasoob01

Yasoob01

Phoenix Channels: Prevent auto-subscription to raw client topic

TL;DR

Clients join a simple topic like "daily_agent_stats:total_inbound_calls".
On the server I expand it using workspace_id + agent_id from params.
But Phoenix still subscribes them to the raw topic too → I want to stop that and only keep the expanded subscription.


Hey folks :waving_hand:

I’m building a Phoenix channel for agent analytics stats.
Here’s a simplified version of my use case:

What I want

  • Clients connect with workspace_id and agent_id as query params.

  • Then they only subscribe with a simplified topic (no need to repeat IDs).

  • On the server, I expand that simplified topic into the full PubSub key and subscribe internally.

Example

Channel:

defmodule MyAppWeb.AgentChannel do
  use Phoenix.Channel

  # Client joins something like: "daily_agent_stats:total_inbound_calls"
  def join("daily_agent_stats:" <> stat_key, _payload, socket) do
    workspace_id = socket.assigns[:workspace_id]
    agent_id = socket.assigns[:agent_id]

    # Expand topic internally
    full_topic = "agent/analytics/daily_agent_stats:#{workspace_id}:#{agent_id}:#{stat_key}"

    IO.puts("Subscribing to #{full_topic}")
    Phoenix.PubSub.subscribe(MyApp.PubSub, full_topic)

    {:ok, socket}
  end

  def join(topic, _payload, _socket) do
    {:error, %{reason: "Invalid topic #{topic}"}}
  end
end

UserSocket:

defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket

  channel "daily_agent_stats:*", MyAppWeb.AgentChannel

  def connect(%{"workspace_id" => ws_id, "agent_id" => agent_id}, socket, _connect_info) do
    {:ok, assign(socket, :workspace_id, ws_id) |> assign(:agent_id, agent_id)}
  end

  def id(_socket), do: nil
end

Client connection (via wscat):

wscat -c "ws://localhost:4000/v1/agent/websocket?workspace_id=c8f4a3a0-835d-4ee2-9c71-0c18550680e5&agent_id=47dbbab6-1abf-4392-a42f-dbde107c2793"

Join a topic without IDs:

{"topic":"daily_agent_stats:total_inbound_calls","event":"phx_join","payload":{},"ref":1}

The Issue:

Even though I subscribe manually inside join/3, Phoenix still also assigns the socket to the original "daily_agent_stats:total_inbound_calls" topic.

So the socket ends up listening on two topics:

  • "daily_agent_stats:total_inbound_calls"

  • "agent/analytics/daily_agent_stats:<workspace_id>:<agent_id>:total_inbound_calls"

I want to disable the first (auto) subscription and only rely on the expanded one.

My Question:
Is there a way to tell Phoenix not to subscribe the socket to the raw topic passed in by the client, so I can fully control subscriptions in join/3?

Any guidance on best practices here would be super helpful.

First 3 of 3 Posts Switch mode

LostKobrakai

LostKobrakai

No. Channels are not an abstraction for communication between only the client and the server (1 to 1). They’re an abstraction to extend phoenix pubsub from just reaching processes on servers to their connected clients – it’s still pubsub on the channels topic.

If you want plain websockets without the semantics of channels take a look at: Bare Websockets | Benjamin Milde

Alternatively you could also make the client join the correct topic directly.

Yasoob01

Yasoob01 OP

Thanks, that makes sense. If I stick with channels, is there a recommended way to avoid exposing sensitive IDs (like workspace_id, agent_id) in the topic string? Or is the expectation that topics are not security boundaries and I should enforce authorization only in join/3?

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Yeah consider topics almost like URLs. In general a secret URL isn’t great security, you instead should perform a real authZ check on join.

— All posts loaded —

Where Next?

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 91561 914
New
byu
@chrismccord : I just saw the Extract AGENTS.md from Phoenix.new into phx.new generator commit to the phoenix project. My initial shotgu...
New
arcanemachine
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
AstonJ
Just a general thread to post chat/news/info relating to AI/ML stuff that may be relevant for Nx now or in the future. Got anything to sh...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement