From 67b2b1203bce8279d9bcda3f34eb0904f9cb7b06 Mon Sep 17 00:00:00 2001 From: John Rees Date: Thu, 23 Jul 2020 17:41:33 +0100 Subject: [PATCH] add JWT auth to socket server --- .env.development | 1 + package.json | 3 +- server.js | 61 ++++++++++++++++++--------------- src/sharedb.ts | 7 +++- yarn.lock | 89 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 123 insertions(+), 38 deletions(-) create mode 100644 .env.development diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..91a08c5 --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +REACT_APP_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.NoZrv5qD6od5nK7CtV_qxMIj2tWyBDkFkNb2hl8Ns3E diff --git a/package.json b/package.json index 9386c9d..9fa98c2 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,12 @@ "private": true, "dependencies": { "@teamwork/websocket-json-stream": "^2.0.0", - "@types/jest": "^24.0.0", "@types/node": "^12.0.0", "@types/react": "^16.9.43", "@types/react-dom": "^16.9.8", "@types/uuid": "^8.0.0", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "random-words": "^1.1.1", "react": "^0.0.0-experimental-4c8c98ab9", "react-dom": "^0.0.0-experimental-4c8c98ab9", @@ -27,7 +27,6 @@ "scripts": { "start": "concurrently 'node server' 'react-scripts start'", "build": "react-scripts build", - "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { diff --git a/server.js b/server.js index bdb699e..04bf532 100644 --- a/server.js +++ b/server.js @@ -1,41 +1,48 @@ const WebSocketJSONStream = require("@teamwork/websocket-json-stream"); +const ShareDB = require("sharedb"); const express = require("express"); const http = require("http"); -const ShareDB = require("sharedb"); const { Server } = require("ws"); +const jwt = require("jsonwebtoken"); -var backend = new ShareDB(); - -// createDoc(startServer); +const backend = new ShareDB(); -// (now doing this in the client instead, so that the ID can be dynamic) -// function createDoc(callback) { -// var connection = backend.connect(); -// var doc = connection.get("examples", "test"); -// doc.fetch(function (err) { -// if (err) throw err; -// if (doc.type === null) { -// doc.create({ nodes: {}, edges: [] }, callback); -// return; -// } -// callback(); -// }); -// } +const JWT_SECRET = "shh"; function startServer() { - // Create a web server to serve files and listen to WebSocket connections - var app = express(); - app.use(express.static("static")); - var server = http.createServer(app); - - // Connect any incoming WebSocket connection to ShareDB - var wss = new Server({ server }); - wss.on("connection", function (ws) { - var stream = new WebSocketJSONStream(ws); - backend.listen(stream); + const app = express(); + + const server = http.createServer(app); + + const wss = new Server({ + server, + verifyClient: (info, cb) => { + // checks if JWT is included in cookies, does not allow connection if invalid + const [, token] = info.req.headers.cookie.match(/Authorization\=([^;]+)/); + + if (!token) { + cb(false, 401, "Unauthorized"); + } else { + jwt.verify(token, JWT_SECRET, (err, decoded) => { + if (err) { + cb(false, 401, "Unauthorized"); + } else { + console.log({ newConnection: decoded }); + info.req.user = decoded; + cb(true); + } + }); + } + }, + }); + + wss.on("connection", function (ws, req) { + const stream = new WebSocketJSONStream(ws); + backend.listen(stream, req.user); }); server.listen(8080); + console.log("Listening on http://localhost:8080"); } diff --git a/src/sharedb.ts b/src/sharedb.ts index 3fb8bff..65f4808 100644 --- a/src/sharedb.ts +++ b/src/sharedb.ts @@ -1,8 +1,13 @@ import ReconnectingWebSocket from "reconnecting-websocket"; import sharedb from "sharedb/lib/client"; +const JWT = process.env.REACT_APP_JWT; + +document.cookie = `X-Authorization=${JWT}; path=/`; + const socket = new ReconnectingWebSocket("ws://localhost:8080"); -const connection = new sharedb.Connection(socket); + +const connection = new sharedb.Connection(socket, {}); export const getConnection = (id) => connection.get("examples", id); diff --git a/yarn.lock b/yarn.lock index 8e8d879..626d9b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1497,13 +1497,6 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@^24.0.0": - version "24.9.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" - integrity sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q== - dependencies: - jest-diff "^24.3.0" - "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" @@ -2559,6 +2552,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -3851,6 +3849,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -5888,7 +5893,7 @@ jest-config@^24.9.0: pretty-format "^24.9.0" realpath-native "^1.1.0" -jest-diff@^24.3.0, jest-diff@^24.9.0: +jest-diff@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== @@ -6392,6 +6397,22 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -6410,6 +6431,23 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: array-includes "^3.1.1" object.assign "^4.1.0" +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -6585,11 +6623,46 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"