How do you access "socket" struct in templates?

I’m experimenting and I don’t get where do you pass Socket in order to access it in a template.
For example with Controller:

def index (conn, _params) do
conn= Plug.Conn.assign(conn, :user, “Bob”)
render conn, “index.html”, conn: conn
end

<%= inspect @conn.assigns[:user] %>

Works and shows the value of the :user key.
What’s the equivalent for socket?

def join(“room:1”, _message, socket) do
socket = Phoenix.Socket.assign(socket, :user, “Alice”)
{:ok, socket}

<%= inspect @socket.assigns[:user] %>

Doesn’t work: assign @socket not available in eex template.
I guess that I must pass it to the template, like “conn: conn” with render, but how does this work with socket?

Socket, as in a Channel I’m guessing, do not use templates (unless you explicitly call a template), they by default just send/receive JSON as per normal broadcast/push things?

I just want to render the “socket” struct that’s been passed around, to see what’s happening.
I’m using a template only for the index.html, and trying to render “socket” in it.
Any idea how I can do that?

Render to… what? If you want to render it to the console then you can just pass it to IO.inspect, if you want to render it to a web-page front-end then you can pass it over a phoenix channel, though that does not sound safe, but then you could stringify it or so… ^.^

Also, what do you mean by the template for index.html, controllers have no ‘socket’ passed around, only channels have a socket.

Right, controllers have Conn struct, channels have Socket struct.

I can IO.inspect a Conn struct in a template if I pass the Conn to the template with “render conn, “index.html”, conn: conn”
So now in “index.html.eex” I can <%= inspect @conn %> and see everything that’s in the current Conn struct.

When using websockets, the connection is upgraded right? And a Socket struct is being passed around instead of Conn struct?
How can I display what’s in that Socket struct on “index.html”?

<%= inspect @socket %> returns error with “assign @socket not available in eex template.”

Push it through the channel. The upgraded connection is another connection, not the http one renders template.

1 Like

Does that mean that the data in the Socket struct is not available for templates and can be used only by the JavaScript client that connects to the server?
So if I "Phoenix.Socket.assign( socket, :user, “Alice”) to the Socket struct, I can access that only from the JS client in the browser?

As far as my understanding goes, there are really two separate flows. The HTTP request-> response flow where your conn has the request and response info, this is stateless which means you cannot persist stuff on the conn for the next request to pick up. On the other hand the websocket connection is a stateful connection which is a connection which is always up between the client and server. This doesn’t have any information about incoming http requests/responses even if they are from the same client. So essentially you have two ways of communicating and one cannot see the other.

Still confused.
This code is supposed to expose a token auth in the client?

<%= tag :meta, name: "channel_token",
               content: Phoenix.Token.sign(@conn, "user", @current_user.id) %>

or

def create(conn, params) do
  user = User.create(params)
  render conn, "user.json",
         %{token: Phoenix.Token.sign(conn, "user", user.id), user: user}
end

But it’s using the @conn struct, not @socket?
If it’s using the @conn struct, which is passed only once per request-response, how does the @socket struct use it in order to authenticate every message sent with websockets from the client?
If the token is stored in @socket struct, and socket struct is passed around on every message, I get it, but the code for token auth is using the @conn struct, which is not passed around with websockets?

@Deithrian, Phoenix.Token.sign takes a context for the first argument, which, as the documentation stated, can be one of:

  • the module name of a Phoenix endpoint - where the secret key base is extracted from the endpoint
  • Plug.Conn - where the secret key base is extracted from the endpoint stored in the connection
  • Phoenix.Socket - where the secret key base is extracted from the endpoint stored in the socket
  • a string, representing the secret key base itself. We recommend a key base with at least 20 characters to provide enough entropy

One thing in common between these four is that they provide a way to retrieve the :secret_key_base config of the app, which is used to both signing and verifying the token. It will then generate a string token based on the salt and payload (2nd and 3rd argument).

Both of your snippets will be run on the server side, one in a template and one in a controller action. There are no reference to sockets in those contexts, so conn can be used. First snippet render the token as a meta tag, which can be retrieved by your JS and sent on establishing connection. The second snippet returns a token as a field in a JSON response, which can also be retrieved by your JS. I imagine the first approach is used if your website is a fully server-rendered, and the second is for when your client-side is a JS app using AJAX. Note that there might be other ways, such as taking from assigns as used in this tutorial.

Details on how to send the token on connection should be available as comments on the newly generated web/static/js/socket.js file in Phoenix projects.

On verification, typically in your web/channels/user_socket.ex, the connect function only has socket struct, and since it also has the reference to your Endpoint (as stated in the docs), it can also be used as the context to Phoenix.Token.verify.

In conclusion, @conn and @socket represents different thing (as many have stated above), but they do have the same reference to your app’s Endpoint, therefore both can be used for authentication with Phoenix.Token. The structs don’t store any token, rather the token is sent on request or socket connection.

Does that make sense?

2 Likes

Ok, so:

  1. User requests HTML page over HTTP and authenticates.
  2. App generates auth token and assigns it to the “conn” struct.
  3. Conn struct is passed to the HTML page and rendered with <%= assigns[:user_token] %> in a tag.
  4. JavaScript picks up the tag contents and sends it as authentication for connect().
  5. Connect checks the token and if valid assigns the token to the Socket struct.
  6. Socket struct now has :current_user with the token value, which represents the user credentials, so they don’t have to send them on every message through websockets.
    Is that correct?

Correct, websockets is a stateful connection. This is all websockets 101 if you want to look at how websockets is implemented and used in browsers. :slight_smile:

I know a few good libraries in a few languages (like C++) if you want to see how it works in the gritty details too. :slight_smile:

1 Like

I think I understand how they work in principal, that’s why I was confused.
I remember reading that the normal request-response Conn struct is supposed to die after the request is over, but the Socket struct lives as long as the websocket connection lives.
I want to try and use websockets for my first ever app, and tried to access the Socket struct in the template, naively, then remembered reading something about using authentication tokens for websocket connections and something about exposing it in the template, but when I found the “exposing” part, it was using Conn instead of Socket, which further confused me :slight_smile:
I guess it’s enough to say that Conn is accessible in templates and Socket is accessible through the JavaScript client.
Hopefully that’s a fair conclusion.

The socket struct does not exist at the time of the request either, at any point, rather it is created when the browser itself initiates ‘another’ connection ‘after’ page load, hence why it is impossible to show any socket data on template render as there is no socket. ^.^

Eh, kind of, but in general it might be more clear as:

  • conn -> A connection from a browser over http to grab a file (like an index page or so)
  • socket -> A connection over a websocket (which is not http, though it is upgraded ‘from’ http it is still originally a new http connection altogether that is dedicated to being upgraded to websocket).

All part of the web-weirdness. ^.^

And the names are just standard, you could call them different things too.

2 Likes