Move token authentication from URL to Phoenix channel join payload

This commit is contained in:
Graham McIntire 2026-01-16 18:22:57 -06:00
parent 538f582fec
commit 9b88a32f33
No known key found for this signature in database
2 changed files with 33 additions and 27 deletions

View file

@ -33,21 +33,32 @@ defmodule ToweropsWeb.AgentChannel do
require Logger
@impl true
def join("agent:" <> agent_token_id, _payload, socket) do
# Verify the agent token ID matches the authenticated socket
if socket.assigns.agent_token_id == agent_token_id do
# Update last_seen_at on join
Agents.update_agent_token_heartbeat(agent_token_id, nil, %{})
def join("agent:" <> _agent_id, %{"token" => token}, socket) do
# Verify agent token from join payload
case Agents.verify_agent_token(token) do
{:ok, agent_token} ->
socket =
socket
|> assign(:agent_token_id, agent_token.id)
|> assign(:organization_id, agent_token.organization_id)
# Send initial job list
send(self(), :send_jobs)
# Update last_seen_at on join
Agents.update_agent_token_heartbeat(agent_token.id, nil, %{})
{:ok, socket}
else
{:error, %{reason: "unauthorized"}}
# Send initial job list
send(self(), :send_jobs)
{:ok, socket}
{:error, _} ->
{:error, %{reason: "unauthorized"}}
end
end
def join("agent:" <> _agent_id, _payload, _socket) do
{:error, %{reason: "missing token"}}
end
@impl true
def handle_info(:send_jobs, socket) do
jobs = build_jobs_for_agent(socket.assigns.agent_token_id)

View file

@ -2,7 +2,8 @@ defmodule ToweropsWeb.AgentSocket do
@moduledoc """
WebSocket endpoint for remote agent communication.
Agents connect to: ws://server/socket/agent?token=<agent_token>
Agents connect to: ws://server/socket/agent/websocket
Authentication token is sent in the channel join payload.
Uses binary Protocol Buffers encoding for all messages.
"""
@ -12,23 +13,17 @@ defmodule ToweropsWeb.AgentSocket do
channel "agent:*", ToweropsWeb.AgentChannel
@impl true
def connect(%{"token" => token}, socket, _connect_info) do
case Towerops.Agents.verify_agent_token(token) do
{:ok, agent_token} ->
socket =
socket
|> assign(:agent_token_id, agent_token.id)
|> assign(:organization_id, agent_token.organization_id)
{:ok, socket}
{:error, _} ->
:error
end
def connect(_params, socket, _connect_info) do
# Allow all connections - authentication happens at channel join
{:ok, socket}
end
def connect(_, _socket, _connect_info), do: :error
@impl true
def id(socket), do: "agent_socket:#{socket.assigns.agent_token_id}"
def id(socket) do
# Return nil before authentication (channel join will set agent_token_id)
case Map.get(socket.assigns, :agent_token_id) do
nil -> nil
agent_token_id -> "agent_socket:#{agent_token_id}"
end
end
end