JWT, websockets and client-server through the web - websocket

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'));
}
});

Related

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

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.

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 send firebase auth tokens to backend server?

I want to identify currently signed-in user on my nodejs server. To do so securely, after a successful sign-in, I have to send the user's ID token to your server using HTTPS.
As in firebase docs
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
If the token is sent to the backend server using AJAX post request then what should be the URL in xhr request var xhr = new XMLHttpRequest(); xhr.open('POST', url , true); and how to recieve it on nodejs backend server app.js file.
Or there is any other method to do it?
You can add an authorization header in request and parse the header value in your nodejs app.
xhr.setRequestHeader('Authorization', firebaseTokenId);
In your nodejs application you can do:
function abc(req, res) {
authHeader = req.get('authorization');
}

Updating authorization header in websocket graphql

I am trying to implement authentication in Graphql using firebase and websockets (on react native).
The client uses the firebase to authenticate and gets a token. It then sends the token to the server over a websocket client, which validates the user using the admin sdk.
I am facing two problems:
When the app boots up, it establishes a ws connection which by that time, it has no authorization header. The user gets a token after a while using firebase.
The token expires after some time, so after a while I need to update the authorization header in the websocket connection, and re-run the query, mutation or subscription which got rejected because of the expired token.
Is there a way to update the authorization header and re-run the query?
Do I need to close the previous connection and open a new one using the new token in the authorization header? How is this done?
I am using apollo-server, apollo-client, apollo-link, subscriptions-transport-ws.
I haven't run into your exact issue before, but you should check out connectionParams field. If on startup a new websocket client is created, you can fetch a new token asynchronously in the connectionParams.
import { createClient } from 'graphql-ws';
export const createWebsocketClient = (user) => createClient({
url: 'ws://localhost:8080/v1/graphql',
connectionParams: async () => {
const token = await user.getToken();
return {
headers: {
Authorization: `Bearer ${token}`,
},
};
},
});
The token is only sent when initializing the connection, so even if the token expires after the initializing, it shouldn't be a problem.

SignalR and OpenId Connect

I have a server which uses ASP.NET Core Web Api and OpenIddict as authorization framework. Now I've added an SignalR host and want to add authorisation to it.
From different sources I found that SignalR (JS Client) wants that you send the access token in the querystring or by cookie as websockets don't support headers.
As the authentication middleware doesn't check the querystring or cookie container for an authorization entry I need to implement such an provider/retriever/resolver which reads this value by myself.
I've found a solution for IdentityServer but nothing about OpenIddict.
Where/How do I implement such an token resolver with OpenIddict?
If you use JwtBearerAuthentication then you can use OnMessageReceived to set token:
Events = new JwtBearerEvents()
{
OnMessageReceived = async (ctx) =>
{
ctx.Token = ctx.Request.Query["<qs-name>"];
}
}
Or if you use IdentityServerAuthentication then you can use TokenRetriever(not tested but it should be something like this):
TokenRetriever = (ctx) =>
{
return ctx.Request.Query["<qs-name>"];
}
Just like #adem-caglin mentioned, in IdentityserverAuthentication you use TokenRetriever and can go with the built-in functions if what you're after is the standard bearer header or a query string
TokenRetriever = (request) =>
{
// by default calls TokenRetrieval.FromAuthorizationHeader()(request);
// check if request is to signalr endpoint and only then apply FromQueryString
return TokenRetrieval.FromQueryString()(request);
}

Resources