There are generally two basic ways for a client to send a user:pass combo or token on attempting to start a websocket with an Elixir server.
I am testing initially with Javascript client just for the sake of it and simplicity.
1) URL Parameters (not working):
In theory a Javascript client could do something like this:
const username = 'yourUsername'; const password = 'yourPassword';
const encodedCredentials = btoa('${username}:${password}');
const sock = new WebSocket('ws://localhost:5001/websocket?auth=${encodedCredentials}');
Or supersimple for testing, just run via F12 in web browser console and check Elixir for response:
sock = new WebSocket("ws://localhost:5001/websocket?auth=whatever");
In theory the websocket it set up like this should be able to parse this like:
get "/websocket" do
#check headers or params for token or user pass
IO.inspect(conn.req_headers)
IO.inspect(conn.params)
IO.inspect(conn.path_params)
IO.inspect(conn.query_params)
IO.puts("FINISHED INSPECTING HEADERS");
#try to login with this info and if successful proceed:
conn
|> WebSockAdapter.upgrade(My.UserWebSock, [], timeout: 60_000)
|> halt()
end
However, this doesn’t seem to find the query parameters. I can see my Elixir gets the headers in full, but none of IO.inspect(conn.params)
IO.inspect(conn.path_params)
or IO.inspect(conn.query_params)
show the added auth info. They are all inspected as empty maps %{}
Why is this? Any solution for this approach? Why might the URL parameters not be going through?
2) Use SEC-WEBSOCKET-PROTOCOL field:
The other approach is you can use the free field of the WebSocket protocol like this:
const base64auth = btoa("user:pass");
const ws = new WebSocket("ws://localhost:5001/websocket", ["userpass", base64auth])
and/or
const base64token = btoa(token);
const ws = new WebSocket("ws://localhost:5001/websocket", ["token", base64token])
This free field takes a list of strings and shows up in Elixir easily under IO.inspect(conn.headers)
so this at least works. You can then just have a case for checking if first entry is “userpass” or “token” to handle them differently on the Router there.
This at least works. In one way it is better also as the user’s system (if web browser) won’t store any sensitive info in the browser history (as it is not part of the url).
Thoughts?
3) Bad option not worth discussing
(The third option is to start the websocket freely and then request the token/auth after but this is resource foolish in case of attack so not worth discussing further in my opinion.)
What do you think? How do you usually do this? Why is option #1 not working? Thanks for any help.