Currently, we are using a fairly standard setup with Node.js + Passport + AJAX as the webserver, Backbone on the client, and an app server exposing APIs for the back-end. The client makes AJAX request to Node, which does some housecleaning such as checking the session authentication, then passes the request along to the app server.
We are experimenting with replacing the AJAX portion between the client and Node with WebSockets. That part I have working fine. Previously we were using passport to check if the request was authenticated. With a standard Node route, the request and response is always included so this is a trivial task, but I'm not sure how to get it from the socket.
var server = http.createServer(app).listen(port);
var io = socketio.listen(server);
var namespaced = io.of('/socket/test');
namespaced.on('connection', function (socket) {
console.log('a user connected to /socket/test');
socket.on('fetch', function (args) {
// the client has sent a socket message which will functionally replace an AJAX request
// authenticate session here - how to get the request??
});
});
Related
I have an electron app that creates a websocket connection to a node js server. It sends a JSON request to that server telling it to create a xmpp client.
let message = {
action: "setupXmpp",
data: {
username,
password,
},
};
socket.send(JSON.stringify(message));
Within that server I have a switch that reads the message action and creates the xmpp client. The code in xmppActions is standard boilerplate taken from xmpp's repo
const xmppActions = require("./Webapp/xmppActions");
case "setupXmpp":
console.log(`Received setupXmpp request`);
var { username, password } = message.data;
const xmpp = xmppActions.setUpXMPPconn(username, password);
xmpp.on("online", async (address) => {
console.log("▶", "online as", address.toString());
ws.send("Register xmpp Success!");
});
break;
Everything works fine I can create an xmpp client and send messages, all good.
My issue is when i have two clients open and they both register (with different username and password ) whoever is the last request always overrides the previous register. I've done a wireshark trace and the two websocket connections are created as I would expect but when it comes to sending messages they both use the most recent register. I assume it's because the XMPP client is a constant and whoever is last it uses those for all future requests.
How do I make it so that each websocket connection gets its own XMPP client almost like a request scoped client specific for each websocket.
I had a constant outside the websocket connection, changed it to have a var inside so each connection had it own client.
This might be opinion based, but I still wonder is there a best practice since I'm almost clueless about websocket practices
I have a SPA that gets a JWT token from my own OP. It then uses that JWT to connect to other services I own using both REST and WebSockets.
As far as it goes for REST, it's pretty straightforward:
The REST API validates the JWT (sent in Authorization: Bearer ...) and provides access to the protected resource or responds with a 401, letting the SPA know it needs to request a new token.
Now with websockets :
During the load of the SPA, once I got a token, I'm opening a WS to my webservice. First message I send is a login_message with my JWT, which then I keep on server's websocket instance to know who's sending the messages.
Each subsequent message I receive, I validate the JWT to see if it's expired.
Once it has expired as far as I understand, I'm facing two options :
Drop the websocket with a token_expired error of some kind and force the browser to establish a new websocket connection once the token get refreshed.
Keep the websocket open, return an error message and send a new login message (once token is refreshed)
Don't use a login message but just send the JWT in each request.
Question : Which method would you recommend and why? In terms of security, and performance. Are there any other common practice I did not listed?
Quite an old question I've asked, so I'd be happy to share our chosen practice:
Once the client gets his JWT for the first time (when the application starts), a WebSocket is opened.
To authenticate the channel, we send a message that we define as part of our protocol, called authMessage which contains that JWT.
The server stores this data on the socket's instance and verifies it's validity/expiry before sending data down the wire or receiving from the client.
The token gets refreshed silently in web application minutes before it is expired and another authMessage is issued to the server (repeat from step 2).
If for whatever reason it gets expired before getting renewed, the server closes that socket.
This is roughly what we have implemented in our application (without optimization) and worked really well for us.
Oauth2 flow has two options to renew the token. As you said on of these options is prompt a message to the use to enforce a new login process.
The other option is to use the refresh_token in which you will avoid this login process to your user, every time the session expires and renew the token in a silent way.
In both case, you need to store the jwt in the client (commonly after login) and update it (after interactive login or silent regeneration). Localstorage, store or just a simple global variable are alternatives to handle the store and update the jwt in he client.
As we can see, jwt regeneration is solved following oauth2 spec and is performed at client side, SPA in your case.
So the next question is: How can I pass this jwt (new or renewed) to the external resources (classic rest api or your websocket)?
Classic Rest Api
In this case as probably you know, send the jwt is easy using http headers. In each http invocation we can send the old/valid jwt or the renewed jwt as header, commonly Authorization: Bearer ...
Websocket
In this case it's not so easy because according to a quickly review, there are not a clear way to update headers or any other "metadata" once the connection was established:
how to pass Authorization Bearer access token in websocket javascript client
HTTP headers in Websockets client API
What's more, there is no concept of headers, so you need to send this information (jwt in your case) to your websocket using:
protocols
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]);
cookies
document.cookie = 'MyJwt=' + jwt + ';'
var ws = new WebSocket(
'wss://localhost:9000/wss/'
);
simple get parameters
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");
Websocket only receive text
And according to the following links, websocket can extract header, get parameters and protocol just at the stabilization stage:
https://medium.com/hackernoon/implementing-a-websocket-server-with-node-js-d9b78ec5ffa8
https://www.pubnub.com/blog/nodejs-websocket-programming-examples/
https://medium.com/#martin.sikora/node-js-websocket-simple-chat-tutorial-2def3a841b61
After that, websocket server only receive text:
const http = require('http');
const WebSocketServer = require('websocket').server;
const server = http.createServer();
server.listen(9898);
const wsServer = new WebSocketServer({
httpServer: server
});
wsServer.on('request', function(request) {
const connection = request.accept(null, request.origin);
connection.on('message', function(message) {
//I can receive just text
console.log('Received Message:', message.utf8Data);
connection.sendUTF('Hi this is WebSocket server!');
});
connection.on('close', function(reasonCode, description) {
console.log('Client has disconnected.');
});
});
Send jwt in each request
Having analyzed the previous topics, the only way to send the new o renew token to your websocker backend is sending it in each request:
const ws = new WebSocket('ws://localhost:3210', ['json', 'xml']);
ws.addEventListener('open', () => {
const data = {
jwt: '2994630d-0620-47fe-8720-7287036926cd',
message: 'Hello from the client!'
}
const json = JSON.stringify(data);
ws.send(json);
});
Not covered topics
how perform a jwt regeneration using refresh_token
how handle silent regeneration
Let me know if you need this not covered topics.
I'm picking my way through the dartiverse_search example from the welcome page in dart editor. I see that it uses a path route to decide whether to transform a request into a websocket:
// The client will connect using a WebSocket. Upgrade requests to '/ws' and
// forward them to 'handleWebSocket'.
router.serve('/ws')
.transform(new WebSocketTransformer())
.listen(handleWebSocket);
Is it possible to turn a request into a websocket without using a routing path, for example using a query string to the root url?
You can specify any condition for upgrading to a WebSocket connection. You can even upgrade any connection request to a WebSocket connection without specifying a condition like this:
WebSocketTransformer.upgrade(request).then((WebSocket websocket) {
websocket.listen((String text) {
// process sent data
});
websocket.add(JSON.encode("Hello"));
});
If the request is not a valid web socket upgrade request a HTTP response with status code 500 will be returned. Otherwise the returned future will complete with the [WebSocket] when the upgrade process is complete.
I'm trying to build a chat application based on sails.js. The url for messages from a specific chat looks like this:
/api/chat/:id/messages
When I request this url with XHR, it provides a session cookie and sails.js builds a session object. I can easily check user rights to read the messages from the specific chat.
However, I need to request this url with socket.io so that the client can subscribe to all future changes of the messages collection.
When I request this url with socket.io, no session cookie is set and the sails.js session is empty. So, I cannot check the user rights on the server-side.
I do understand that socket requests are not HTTP-requests. They don't provide any cookies on their own.
Is there any simple workaround?
I found a way to get the session object which was set while socket.io handshaking.
In your controller, you should do something like this:
myControllerAction: function(req, res) {
var session = req.session;
if (req.isSocket) {
var handshake = req.socket.manager.handshaken[req.socket.id];
if (handshake) {
session = handshake.session;
}
}
//session now contains proper session object
}
You can implement this in sails.js policy, and attach this policy to some controllers. But don't write you socket session into req.session! Otherwise, you'll get an error trying to respond to the client (original req.session is still used in some way). Instead, save it as req.socketSession or something like that.
please send a JSONP request from your application before sending a socket request,that will create a cookie and accepts socket requests.
You can do your initial login over the socket.post() instead of XHR, subsequent socket requests will be authorized.
alevkon,in the above mentioned method you have to implement the same in all the controllers,because you don't know which controller is accessed for the first time...but by sending just one jsonp request you can create a cookie between client and server,the same cookie is used until the next session.
There's a lot going on here so I'll simplify this into a pseudo example. Forget about security and whatnot for a minute here. The point is to understand the functionality.
Let's say I'm running a local web server with node.js to dev a website. In the website, the user should be able to create a new account. The account information will be submitted via ajax to the node server. I then need the node server to take the incoming request and pass it along to another server that gives me access to a database. CouchDB, for example.
So here's a pseudo example of what I'd like to happen.
In the client's browser:
$.ajax({
url: './database_stuff/whatever', // points to the node web server
method: 'POST',
data: {name: 'Billy', age: 24}
});
In the Node web server:
var http = require('http'),
dbServer = 'http://127.0.0.1:5984/database_url';
http.createServer(function (req, res) {
/* figure out that we need to access the database then... */
// magically pass the request on to the db server
http.magicPassAlongMethod(req, dbServer, function (dbResponse) {
// pass the db server's response back to the client
dbResponse.on('data', function (chunk) {
res.end(chunk);
});
})
}).listen(8888);
Make sense? Basically what's the best way to pass the original request along to another server and then pass the response back to the client?
If the server at dbServer url supports streaming you could do something like
var request = require('request');
req.pipe(request.post(dbServer)).pipe(res)
where request is a module, for more info look here https://github.com/mikeal/request
This is quite readable and easy to implement, if for whatever reason you cannot do this then you could take what you need from the request and manually POST it, then take the response and res.send it to the client.
Sorry if there's an error in my code, I haven't tested it but my point should be clear, if it's not then ask away.