How to use with Channels or Live View? View Source
First add Haytni.LiveViewPlugin
to your Haytni stack in lib/your_app/haytni.ex by appending the following line:
stack Haytni.LiveViewPlugin
Then, in your endpoint (lib/your_app_web/endpoint.ex), you should find something like that:
socket "/socket", YourAppWeb.UserSocket,
websocket: true,
longpoll: false
Make sure to change the true
values here to [connect_info: [:peer_data, :x_headers]]
.
TODO (call from connect/3 callback)
# lib/your_app_web/channels/user_socket.ex
defmodule YourAppWeb.UserSocket do
@impl Phoenix.Socket
def connect(params, socket, connect_info) do
Haytni.LiveViewPlugin.connect(YourApp.Haytni, params, socket, connect_info)
end
# ...
end
Now, in your javascript asset file, to acquire a token and reinject it to be granted to create a connection, use something like the following:
fetch(
'/token', {
method: 'POST',
headers: {
'Accept': 'application/json',
},
}
)
.then(
response => response.json()
).then(
token => {
let socket = new Socket("/socket", {transport: WebSocket, params: {token: token}})
socket.connect()
/* ... */
}
)
fetch
above can be replaced by a regular Ajax (XMLHttpRequest) call if your prefer.
'/token'
is the value of Routes.haytni_<scope>_token_path/[23]
if you do a mix phx.routes
. This path can overriden by the :token_path
key when you call YourApp.Haytni.routes/1
in your router.
Configuration
If Phoenix is behind nginx or any proxy
Tokens for channels and liveview are short lived and validated against current IP address. But IP addresses from peer_data might be wrong if Phoenix does not directly handles HTTP and sockets. If this is your case, you need to specify the name (in lower case!) of the HTTP header your proxy follows you client addresses as :remote_ip_header
option of stack Haytni.LiveViewPlugin
.
For example, if you use nginx and it is configured with:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
So you have to write:
stack Haytni.LiveViewPlugin, remote_ip_header: "x-forwarded-for"
Proper disconnection of channels and Live View
For proper logout to channels and live view, you need to write the Phoenix.Socket.id/1
callback and, if last one does not return the string "user_socket:#{socket.assigns.current_<scope>.id}"
, you'll need to write a function somewhere and give it (via capture) as :socket_id
option. Let's see an example:
# lib/your_app_web/channels/user_socket.ex
defmodule YourAppWeb.UserSocket do
use Phoenix.Socket
# ...
@impl Phoenix.Socket
def id(socket) do
"user_socket:#{socket.assigns.current_user.id}"
end
end
stack Haytni.LiveViewPlugin, socket_id: &("user_socket:#{&1.id}")