Open pwnall opened 9 years ago
Taking cue from https://github.com/websockets/ws/issues/377#issuecomment-416248904 I was able to pass decoded JWT token from verifyClient by setting the value into request.headers object and retrieving it in connection event's request object.
I am not sure if this is the right way to do it, but this works.
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({
port: 8080, verifyClient: async ({ req }, cb) => {
const { headers } = req;
if (headers.token) {
const decodedToken = await decodeToken(headers.token);
req.headers.decodedToken = JSON.stringify(decodedToken);
cb(true);
} else {
cb(false, 401, 'Unauthorized');
}
}
});
wss.on('connection', (ws, req)=> {
console.log("on Connection decodedToken ==> ", JSON.parse(req.headers.decodedToken));
});
Why not just attach the object to the request with som resonably named property? This is what JS is made for 😃
From my socket connection, I'm trying to set a jwt and send it back as an http response, or somehow set the cookie of the client. Am I in the right thread? Is there a recommended method for this?
From my socket connection, I'm trying to set a jwt and send it back as an http response, or somehow set the cookie of the client. Am I in the right thread? Is there a recommended method for this?
I think this is what headers
event is for take a look at https://github.com/websockets/ws/blob/master/lib/websocket-server.js#L387 and line 352. But why go this way if you can just pass the JWT back to the client with a simple message
From my socket connection, I'm trying to set a jwt and send it back as an http response, or somehow set the cookie of the client. Am I in the right thread? Is there a recommended method for this?
I think this is what
headers
event is for take a look at https://github.com/websockets/ws/blob/master/lib/websocket-server.js#L387 and line 352. But why go this way if you can just pass the JWT back to the client with a simple message
@sdrsdr Thank you, I will give it a try. Meanwhile can you explain what you meant by passing JWT back to client with a simple message? What kind of message?
You create websocket connection to pas messages between peers a browser an a server or two servers. The establishment of the connection folows fixed rules but after that you can send anything you like to your peer, including the JWT token, and the peer can handle it as it's most cinvinient
Hi. Sorry, probably stupid question, but It is suggested to use
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n")
socket.destroy()
It is destroyed, but how to get HTTP/1.1 401 Unauthorized
on client?
On client there is simple code.
const ws = new WebSocket("ws://127.0.0.1:8080/")
ws.onerror = (event) => {
console.log("WebSocket onerror", event)
}
ws.onclose = (event) => {
console.log("WebSocket onclose", event)
}
Dev console (Network/WS) has Request, but no Response tab
@rooton You can find a bunch of examples: Suggested handleUpgrade and connection flow
Hello @trasherdk:
Thanks for the examples.
Out of curiosity, I checked your repo https://github.com/trasherdk/ws and saw that you mention:
This branch is up to date with websockets/ws:master.
Could you please share the motivation for your branch?
Thanks.
@rooton You can find a bunch of examples: Suggested handleUpgrade and connection flow
Thank you, but you are just copy/paste example. And my question is about socket.write. It is impossible to read message on client side to ensure thats an auth error.
my 5 cents: verifyClient is the only place to deploy connection rate limits, even tough it is not in the spec it is the ideal place to ban connections with 429 before authentication
@farr64 The reason for my clone/fork is test snippets
It's pretty much answers to other peoples issues, routed into folders on my mail-server. This way I don't have to look for answers in closed issues, but have a collection of stuff I find interesting.
The trasherdk-snippets
branch was to avoid locked issue template, but opted for discussions later :smile:
@constantind You are probably right about the rate-limit and verifyClient thingy. It makes sense to do that as early as possible.
@trasherdk wrote:
@rooton You can find a bunch of examples: Suggested handleUpgrade and connection flow
I'm trying to figure out how to get an authentication failed
close reason from a browser WebSocket client, just like @rooton did, and the linked examples don't answer this at all. The browser websocket doesn't produce a http response of any sort that you can inspect for status codes, so socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n")
and socket.destroy()
will just kill the websocket without giving any close reason to the browser websocket.
I've moved the authentication handling inside the wss.handleUpgrade
like this because the ws.close call can respond with a code and reason that the websocket.onclose handler on the browser websocket will actually get.
httpServer.on("upgrade", (request, socket, head) => {
wss.handleUpgrade(request, socket, head, (ws) => {
const authResult = authenticate();
if (!authResult.ok) {
ws.close(4000, "authentication failed");
}
wss.emit("connection", ws, request);
});
});
I don't know if there are real costs/drawbacks to handling it here and not a level above in the httpServer.on("upgrade"
handler. I would love it if anyone could share some wisdom on this.
@samal-rasmussen did you ever determine if there were any cons associated with this? I'm also in the same boat and would love to know what's the best practice here!
Does anyone know how do I get the server response (handshake response) in the client? I can only get 1006 (abnormal closure) in the close
event. Is there really not a way to inspect the handshake response in this library?
EDIT: I had to use the unexpected-response
event
@samal-rasmussen did you ever determine if there were any cons associated with this? I'm also in the same boat and would love to know what's the best practice here!
Works fine so far 🤷♂️
@lpinca Can you comment on the approach @samal-rasmussen is using to actually return failure codes back to a client? I've struggled to see the point of writing an HTTP response code to the socket before destroying it in the http server upgrade
handler because it doesn't appear that the client ever receives any response at all.
Yet such a pattern (writing a response code to the socket) seems to be used in all of the documentation involving authentication.
That is "ok" but in that case the authentication happens after the WebSocket connection is established. The 'open'
event is emitted on the client. If you want to prevent the connection from being established you have to close it during the handshake.
Yes, that appears to be the trade-off. So are you confirming that there is no way to return an error code to the client (at least in a way that a browser can see it) without first establishing a websocket connection?
In the browser client, no. Other clients (like ws
) might allow you to read the HTTP response.
That is "ok" but in that case the authentication happens after the WebSocket connection is established. The
'open'
event is emitted on the client. If you want to prevent the connection from being established you have to close it during the handshake.
Yes. This also means that you cannot assume that you are authenticated when you get the open event on the client. The client must wait for a message from the server than confirms the validation first.
I'm doing some expensive work in
verifyClient
and I'd like to reuse the result in theconnection
event handler.I'm currently using the fact that the undocumented
WebSocket
propertyupgradeReq
is the same request asinfo.req
inverifyClient
, and I'm modifying the request. This feels dirty though.Will you please consider allowing any truthy
verifyClient
result, and passing it into theconnection
event handler?If this seems reasonable, I'd be glad to prepare a pull request.