How to use Secured WebSocket (WSS) in stand-alone Apollo application? - apollo-server

Apollo Server 2.0 ships with built-in server as described here. That means no Express integration is required when setting it up, so the implementation looks something like this:
const { ApolloServer, gql } = require('apollo-server');
// Construct a schema, using GraphQL schema language
const typeDefs = gql`
type Query {
announcement: String
}
`;
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
announcement: () =>
`Say hello to the new Apollo Server! A production ready GraphQL server with an incredible getting started experience.`
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
I'm implementing subscriptions to my app. How do I make the app use secured WebSocket protocol with subscriptions? Is it possible at all using the built-in server?
By default the server does not use WSS:
server.listen({ port: PORT }).then(({ url, subscriptionsUrl }) => console.log(url, subscriptionsUrl));
spits out http://localhost:4000/ and ws://localhost:4000/graphql.
In development I got my app to work fine but when I deployed to production I started getting these errors in console:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but attempted to connect to the insecure WebSocket endpoint
'ws://example.com/graphql'. This request has been blocked; this
endpoint must be available over WSS.
and
Uncaught DOMException: Failed to construct 'WebSocket': An insecure
WebSocket connection may not be initiated from a page loaded over HTTPS.

Solved. Apparently configuring the WebSocket URL to start with wss:// instead of ws:// was enough.

Related

Is there a way to have an HTTPS endpoint for apollo react?

Following the apollo react documentation works fine when I have an http url, but when I use an endpoint with SSL then I run into CORS errors.
import { ApolloClient} from 'apollo-boost';
import { HttpLink } from 'apollo-link-http';
const link = new HttpLink({
uri: 'https://mywebsite.dev/graphql',
useGETForQueries: true
});
const client = new ApolloClient({
cache,
link:
});
I get that the httpLink is for http, but I don't see any documentation on how to implement an https link.
I doubt this is a http link issue - is the client in https and does the endpoint in https as well as accept that origin ?

react-scripts proxy a http/https server is loading the web in response

i am having a https create-react-app application(https://xyz.site.com), i am proxing a server which is a different domain, when i am loading the application the api is giving the web html data as a response, there is no hit happened in the server,
i have tried using HTTPS=true in .env file, still i am not able to get the server response
setupProxy.js
module.exports = (app) => {
app.use(
'/api',
createProxyMiddleware({
target: process.env.REACT_APP_API_URL, // https://xxx-alb-23333.us-west-2.elb.amazonaws.com
changeOrigin: true,
}),
);
};

Can't get connection to socket server

I'm fairly new to a lot of this stuff and am trying to figure it out.
I have a hosted domain at <my.domain.com>. I host a game at this address that users can go to that address and the game loads in the browser for them.
On the same server I am running an Express nodejs (we'll call this HTTP SERVER) server to receive HTTP requests.
Also on the same server I am running a socket server using the Socket.io (we'll call this SOCKET SERVER) library.
HTTP SERVER can connect to SOCKET SERVER via localhost:<port> and they can communicate back and forth. I can send requests from my mobile device to HTTP SERVER which forwards those request to SOCKET SERVER and get a response back on the mobile device.
My problem now is I need to create another connection to SOCKET SERVER from my hosted game at <my.domain.com>. However, when I attempt to connect to localhost:<port> like I do from HTTP SERVER I get an ERR_CONNECTION_REFUSED error. I am assuming this has to do with with the host name being different. I've attempted to add
app.use(function(req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
});
But that doesn't seem to help. I'm not really sure where to go from here.
Socket server app.js
const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
});
server.listen(8082);
io.on('connection', (socket) => {
console.log(`Socket server 'connection' event`);
});
Code in HTTP SERVER that does properly connect and send/receive messages
var socket = require('socket.io-client')('http://localhost:8082');
socket.on('connect', () => {
console.log(`HTTP server - 'connect' event to socket server`);
});
This is a javascript file that the game loads as an add-on. Hooks is provided by the game as an EventEmitter. I do not have direct access to the HTML pages the game displays, though I can manipulate them via this javascript add-on file.
let socket;
// a game hook when it's initialized
Hooks.on("init", function() {
// don't have direct access to game pages, so create a script tag and load
// the socket.io client library
const scriptRef = document.createElement('script');
scriptRef.setAttribute('type', 'text/javascript');
scriptRef.setAttribute('onload', 'window.socketLibraryLoaded()');
scriptRef.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js');
document.getElementsByTagName('head')[0].appendChild(scriptRef);
});
// handler for when library is loaded
window.socketLibraryLoaded = () => {
log('Socket library loaded');
// i assume this address is wrong since the host of the game is <my.domain.com> and it's trying to connect to localhost
socket = io('https://localhost:8082');
socket.on('connect', () => {
log('Connected to socket server');
});
socket.on('connect_error', error => {
log(error);
});
}
So after banging my head on the wall for more than 10 hours over this I finally found the issue. And of course a simple user error.
The CORs error wasn't really the problem. I was getting that error because the NGINX proxy was erroring which caused the proper headers not to get sent back so the browser showed that error.
The issue was that in one place in my NGINX configuration I was using 127.0.0.0 instead of 127.0.0.1

How to remove socket.io EIO and other parameters from url

When I configure socket-io with the following options:
{ url: 'ws://localhost:8888', options: {path: '/chatws', transports: ['websocket'], reconnectionAttempts: '3'}}
I get the following error
WebSocket connection to 'ws://localhost:8888/chatws/?EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 501
If I use other frameworks that do not add any parameters then it works.
How does one remove the EIO and all query parameters from the URL?
That's not the case.
While you are implementing only websocket based transportation, you need to configure that in both sides. As by default, socket.io try to establish the connection using long polling.
Server-side implementation:
const express = require('express')()
const server = require('http').createServer(express)
const io = require('socket.io')(server,{transports:['websocket']})
Client Side Implenation :
import io from 'socket.io-client'
const socket = io('http://localhost:8080',{transports: ['websocket']})
This worked for me!

JWT, websockets and client-server through the web

we're building a web service with web server and PC clients. client programs connect to the web server and send some data over websockets.. there is no user interaction in the client, no passwords or logins... it just send some data related to customer's account...
what we're concerned about is should we use JWT for preventing client program or traffic hijacking or not...?
all articles we've read are about browsers <> web servers but not programs <> web servers....
thanks for any thoughts!
That's correct, to avoid other client making request to your websocket server you can use JWT. You don't mentioned what technologies are you using. Here an example with express and socket.io.
1) The client send the jwt on query params.
2) The server verify the jwt signature.
import express from "express";
import socket from "socket.io";
import http from 'http';
const server = http.createServer(app);
const io = socket(server);
io.use( async (socket, next) => {
if(!socket.handshake.query)
next(new Error('Not Authenticated'));
const token = socket.handshake.query.token;
const encUsrId = socket.handshake.query.pcClientToken; //maybe
try{
let hasErrors;
if(token)
hasErrors = await jwt.validateJSON(token); //validate with your own function.
next();
}
catch(ex){
next(new Error('Not a valid JWT'));
}
});

Resources