socketIO over SSL on Smartphone Browser - ssl

I have an Apache webserver with a valid SSL certificate. It runs my web application on it. Let's call it Server A.
Then I have a second server running a Node-Js server with a valid SSL certificate. There also socket.IO runs. And this one we call Server B.
A client requests the web application at server A and gets the desired page displayed. If the page is set up at the client, a connection to server B is established via websockets. If another client should change something on the page, it will be adapted for all currently connected clients.
Websockets work as desired. As long as the page is accessed via a computer browser.
If I now go to the website with my smartphone (Iphone 7) via Safari or Chrome (WLAN), no connection to the websocket server (Server B) is established.
Then I set up a small websocket example on http without encryption.
There the websockets work on the smartphone browser.
I hope I could describe my problem understandably. I am very grateful for hints, examples or similar.
// This script run on my Server
const fs = require('fs');
const server = require('https').createServer({
key: fs.readFileSync('myserver.key', 'utf8'),
cert: fs.readFileSync('myserver.cer', 'utf8'),
passphrase: ''
});
let io = require('socket.io')(server);
server.listen(3003);
io.on('connection', function (socket) {
console.log("User Connected connect " + socket.id);
socket.on('disconnect', function () {
console.log("User has close the browser " + socket.id);
});
socket.on('feedback', function (data) {
io.sockets.emit('feedback', data);
});
});
// On Clientsite
socket = io.connect('wss://adressOfServer:3003', {
// secure: true,
transports: ['websocket'],
upgrade: false,
rejectUnauthorized: false
//Here I have already tried many combinations
});
socket.on('connect_error', function (error) {
// alert(error);
});

Related

Blazor WebAssembly MQTT over websockets not working

I'm trying to implement an mqtt over websocket client subscriber in Blazor using Paho. The problem is it insists on using wss instead of ws and throws an ERR_SSL_PROTOCOL_ERROR error upon connection.
Here's a simplified code block:
var mqtt;
var host = "api.mydomainexample.com";
var port = 1884;
function onConnect(){
console.log("connected ! Now listening for messages ..");
mqtt.subscribe("someTopic");
}
function onFailure(message){
console.log("connection to host failed: " + message);
}
function onMessageArrived(msg){
var message = "Message received on topic '"+ msg.destinationName +"': "+ msg.payloadString;
console.log(message);
}
function mqttConnect() {
console.log("connecting to " + host + " ..");
mqtt = new Paho.MQTT.Client(host, port, clientid);
var options = {
timeout: 3,
onSuccess: onConnect,
onFailure: onFailure,
useSSL: false
};
mqtt.onMessageArrived = onMessageArrived;
mqtt.connect(options);
}
I copied this code into an html page created in notepad, called the function from the html body and ran the file in browser. It worked and subscribed well.
Also I added useSSL: false in the connection options although I didnt have it before but still didnt work.
here's the error I'm having from console:
WebSocket connection to 'wss://api.mydomainexample:1884/mqtt' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR
I also changed my projects launch settings so that it launches as http and not https because based on this answer, I cannot use a ws from a page loaded through https.
Any ideas ? Can't I just connect to a websocket without certificate in blazor?
Ok it turns out that when creating the blazor application, there is an option to 'configure on https' where this option causes requests redirection from http to https and consequently asks for secure wss instead of ws.
Hope this helps someone!

Close express server when client emits to server with Socket.io

Goal: close server when client emits message.
I can confirm client sends response as it logs: 'hit server
Server code for express is:
io.on('connection', (socket) => {
console.log('this is connected');
socket.on('message', function(data){
console.log('hit server');
server.close();
})
});
io.on('error', function() {
console.log('there is an error');
})
server.listen(3000, () => {
console.log('listen on *:3000');
});
To stop an Express server that you're using socket.io with, you first call:
server.close();
That stops it from accepting new http connections. You can then iterate existing socket.io connections and close each of them:
const sockets = await io.fetchSockets();
for (let socket of sockets) {
socket.disconnect(true);
}
This could, in theory, leave a few regular http connections that are still in process, but will eventually complete or timeout.
server.getConnections(callback)
will tell you if there are any more outstanding connections or not.
If you just want to shut everything down, including your app, you can call:
process.exit()

Socket.io will not accept connection

Working on socket.io for the first time and trying to get it up and going, I can make the request and I have the server up and going, here is the server in node.
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
app.get("/",function (req,res){
res.send("Hello you socket loving bastard!");
});
io.on('connection', socket => {
console.log('user connection', socket);
io.emit('You got someone!', {user: "me"});
});
io.on('close', socket => {
console.log(socket);
});
http.listen(9090, () => {
console.log("Node starting on 9090 for websockets!")
});
Using vue-native-websocket I have this ...
Vue.use(Socket, 'ws://localhost:9090/', {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1500
});
The console in the browser says:
build.js?b408:1 WebSocket connection to 'ws://localhost:9090/' failed: Connection closed before receiving a handshake response
The server says nothing in the console at all, however, it will serve the get request
Well... the issue is that I'm using vue-native-websocket Socket.io is NOT a native websocket handler and adds extra header information which was lacking apparently. I switches to just using ws in node and it works fine.
From the Socket.io docs.
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the packet id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server either.

How to pass query parameters in react native WebSocket connection

I have a server where I can make a WebSocket connection. When a device makes a WebSocket connection (when the app starts/splash screen), the server looks for the query parameter in the URL, which has the auth token of the user and if the token is correct, the connection is approved, else connection is rejected.
This is how the code looks on the react native client side:
const socket = new WebSocket(`ws://${website}/?token=${value}`);
socket.onmessage = (messageEvent) => {
console.log('>>> socket on message:', messageEvent.data);
};
socket.onopen = function () {
socket.send("hello world connection");
};
socket.onclose = () => {
console.log('Socket disconnected');
};
This works fine in my iOS emulator, and the iOS emulator has a WebSocket connection to mine server. However, I have an android phone (testing via LAN), and when I open the app, I get an error saying:
Unable to set ws://192.xxx.xx.xx/token=aut sfiseft2sefsefs..... as default origin header
But, if I remove the query param from the URL
const socket = new WebSocket(`ws://${website}`);
This works fine, but since I cannot pass the token in the query params, I get an error from the server.
How can I pass query parameters to the WebSocket connection in android? What am I doing wrong?
I have the same issue. Solve the issue by using encodeURI() before passing the url to WebSocket.
const uri = encodeURI(`ws://${website}/?token=${value}`);
const socket = new WebSocket(uri);
This works for me.

Socket.IO Authentication

I am trying to use Socket.IO in Node.js, and am trying to allow the server to give an identity to each of the Socket.IO clients. As the socket code is outside the scope of the http server code, it doesn't have easy access to the request information sent, so I'm assuming it will need to be sent up during the connection. What is the best way to
1) get the information to the server about who is connecting via Socket.IO
2) authenticate who they say they are (I'm currently using Express, if that makes things any easier)
Use connect-redis and have redis as your session store for all authenticated users. Make sure on authentication you send the key (normally req.sessionID) to the client. Have the client store this key in a cookie.
On socket connect (or anytime later) fetch this key from the cookie and send it back to the server. Fetch the session information in redis using this key. (GET key)
Eg:
Server side (with redis as session store):
req.session.regenerate...
res.send({rediskey: req.sessionID});
Client side:
//store the key in a cookie
SetCookie('rediskey', <%= rediskey %>); //http://msdn.microsoft.com/en-us/library/ms533693(v=vs.85).aspx
//then when socket is connected, fetch the rediskey from the document.cookie and send it back to server
var socket = new io.Socket();
socket.on('connect', function() {
var rediskey = GetCookie('rediskey'); //http://msdn.microsoft.com/en-us/library/ms533693(v=vs.85).aspx
socket.send({rediskey: rediskey});
});
Server side:
//in io.on('connection')
io.on('connection', function(client) {
client.on('message', function(message) {
if(message.rediskey) {
//fetch session info from redis
redisclient.get(message.rediskey, function(e, c) {
client.user_logged_in = c.username;
});
}
});
});
I also liked the way pusherapp does private channels.
A unique socket id is generated and
sent to the browser by Pusher. This is
sent to your application (1) via an
AJAX request which authorizes the user
to access the channel against your
existing authentication system. If
successful your application returns an
authorization string to the browser
signed with you Pusher secret. This is
sent to Pusher over the WebSocket,
which completes the authorization (2)
if the authorization string matches.
Because also socket.io has unique socket_id for every socket.
socket.on('connect', function() {
console.log(socket.transport.sessionid);
});
They used signed authorization strings to authorize users.
I haven't yet mirrored this to socket.io, but I think it could be pretty interesting concept.
I know this is bit old, but for future readers in addition to the approach of parsing cookie and retrieving the session from the storage (eg. passport.socketio ) you might also consider a token based approach.
In this example I use JSON Web Tokens which are pretty standard. You have to give to the client page the token, in this example imagine an authentication endpoint that returns JWT:
var jwt = require('jsonwebtoken');
// other requires
app.post('/login', function (req, res) {
// TODO: validate the actual user user
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// we are sending the profile in the token
var token = jwt.sign(profile, jwtSecret, { expiresInMinutes: 60*5 });
res.json({token: token});
});
Now, your socket.io server can be configured as follows:
var socketioJwt = require('socketio-jwt');
var sio = socketIo.listen(server);
sio.set('authorization', socketioJwt.authorize({
secret: jwtSecret,
handshake: true
}));
sio.sockets
.on('connection', function (socket) {
console.log(socket.handshake.decoded_token.email, 'has joined');
//socket.on('event');
});
The socket.io-jwt middleware expects the token in a query string, so from the client you only have to attach it when connecting:
var socket = io.connect('', {
query: 'token=' + token
});
I wrote a more detailed explanation about this method and cookies here.
Here is my attempt to have the following working:
express: 4.14
socket.io: 1.5
passport (using sessions): 0.3
redis: 2.6 (Really fast data structure to handle sessions; but you can use others like MongoDB too. However, I encourage you to use this for session data + MongoDB to store other persistent data like Users)
Since you might want to add some API requests as well, we'll also use http package to have both HTTP and Web socket working in the same port.
server.js
The following extract only includes everything you need to set the previous technologies up. You can see the complete server.js version which I used in one of my projects here.
import http from 'http';
import express from 'express';
import passport from 'passport';
import { createClient as createRedisClient } from 'redis';
import connectRedis from 'connect-redis';
import Socketio from 'socket.io';
// Your own socket handler file, it's optional. Explained below.
import socketConnectionHandler from './sockets';
// Configuration about your Redis session data structure.
const redisClient = createRedisClient();
const RedisStore = connectRedis(Session);
const dbSession = new RedisStore({
client: redisClient,
host: 'localhost',
port: 27017,
prefix: 'stackoverflow_',
disableTTL: true
});
// Let's configure Express to use our Redis storage to handle
// sessions as well. You'll probably want Express to handle your
// sessions as well and share the same storage as your socket.io
// does (i.e. for handling AJAX logins).
const session = Session({
resave: true,
saveUninitialized: true,
key: 'SID', // this will be used for the session cookie identifier
secret: 'secret key',
store: dbSession
});
app.use(session);
// Let's initialize passport by using their middlewares, which do
//everything pretty much automatically. (you have to configure login
// / register strategies on your own though (see reference 1)
app.use(passport.initialize());
app.use(passport.session());
// Socket.IO
const io = Socketio(server);
io.use((socket, next) => {
session(socket.handshake, {}, next);
});
io.on('connection', socketConnectionHandler);
// socket.io is ready; remember that ^this^ variable is just the
// name that we gave to our own socket.io handler file (explained
// just after this).
// Start server. This will start both socket.io and our optional
// AJAX API in the given port.
const port = 3000; // Move this onto an environment variable,
// it'll look more professional.
server.listen(port);
console.info(`🌐 API listening on port ${port}`);
console.info(`🗲 Socket listening on port ${port}`);
sockets/index.js
Our socketConnectionHandler, I just don't like putting everything inside server.js (even though you perfectly could), especially since this file can end up containing quite a lot of code pretty quickly.
export default function connectionHandler(socket) {
const userId = socket.handshake.session.passport &&
socket.handshake.session.passport.user;
// If the user is not logged in, you might find ^this^
// socket.handshake.session.passport variable undefined.
// Give the user a warm welcome.
console.info(`⚡︎ New connection: ${userId}`);
socket.emit('Grettings', `Grettings ${userId}`);
// Handle disconnection.
socket.on('disconnect', () => {
if (process.env.NODE_ENV !== 'production') {
console.info(`⚡︎ Disconnection: ${userId}`);
}
});
}
Extra material (client):
Just a very basic version of what the JavaScript socket.io client could be:
import io from 'socket.io-client';
const socketPath = '/socket.io'; // <- Default path.
// But you could configure your server
// to something like /api/socket.io
const socket = io.connect('localhost:3000', { path: socketPath });
socket.on('connect', () => {
console.info('Connected');
socket.on('Grettings', (data) => {
console.info(`Server gretting: ${data}`);
});
});
socket.on('connect_error', (error) => {
console.error(`Connection error: ${error}`);
});
References:
I just couldn't reference inside the code, so I moved it here.
1: How to set up your Passport strategies: https://scotch.io/tutorials/easy-node-authentication-setup-and-local#handling-signupregistration
This article (http://simplapi.wordpress.com/2012/04/13/php-and-node-js-session-share-redi/) shows how to
store sessions of the HTTP server in Redis (using Predis)
get these sessions from Redis in node.js by the session id sent in a cookie
Using this code you are able to get them in socket.io, too.
var io = require('socket.io').listen(8081);
var cookie = require('cookie');
var redis = require('redis'), client = redis.createClient();
io.sockets.on('connection', function (socket) {
var cookies = cookie.parse(socket.handshake.headers['cookie']);
console.log(cookies.PHPSESSID);
client.get('sessions/' + cookies.PHPSESSID, function(err, reply) {
console.log(JSON.parse(reply));
});
});
use session and Redis between c/s
Server side
io.use(function(socket, next) {
// get here session id
console.log(socket.handshake.headers.cookie); and match from redis session data
next();
});
this should do it
//server side
io.sockets.on('connection', function (con) {
console.log(con.id)
})
//client side
var io = io.connect('http://...')
console.log(io.sessionid)