import WebSocket = require('ws'); import http = require('http'); // import authing = require('authing-js-sdk'); import firebaseApp = require('firebase-admin/app'); import firebaseAuth = require('firebase-admin/auth'); import LRUCache = require('lru-cache'); import nanoid = require('nanoid'); import { URL } from 'url'; import { handleConnection } from './utils'; if (process.env.NODE_ENV !== 'development') { firebaseApp.initializeApp({ credential: firebaseApp.cert({ clientEmail: process.env.FIREBASE_ACCOUNT, privateKey: process.env.FIREBASE_CERT, projectId: process.env.FIREBASE_PROJECT, }), projectId: process.env.FIREBASE_PROJECT, }); } const _getWorkspace = (path: string) => { const [_, part1] = path.split('/collaboration/'); const [workspace] = part1?.split('/') || []; return workspace; }; const AFFINE_COMMON_WORKSPACE = 'affine2vin277tcmafwq'; const _checkAuth = async ( request: http.IncomingMessage, response: http.ServerResponse, callback: (response: http.OutgoingMessage, workspace: string) => boolean ) => { const url = new URL(request.url, `http://${request.headers.host}`); const workspace = _getWorkspace(url.pathname); if (process.env.NODE_ENV === 'development') { if (workspace) return callback(response, workspace); return false; } try { const decodedToken = await firebaseAuth .getAuth() .verifyIdToken(request.headers.token as string); const allowWorkspace = [AFFINE_COMMON_WORKSPACE, decodedToken.uid]; if (allowWorkspace.includes(workspace)) { return callback(response, workspace); } } catch (error) { console.log(error); } return false; }; const HOST = process.env.HOST || 'localhost'; const PORT = process.env.PORT || 3000; const _tokens = new LRUCache({ max: 1024 * 10, ttl: 1000 * 60 * 5, }); const _server = http.createServer((request, response) => { if ( request.method === 'POST' && typeof request.headers.token === 'string' ) { _checkAuth(request, response, (response, workspace) => { const protocol = nanoid.nanoid(16); _tokens.set(protocol, workspace); response.end(JSON.stringify({ protocol })); return true; }) .then(responded => { if (!responded) { response.writeHead(401).end(); } }) .catch(error => { console.log(error); response.writeHead(401).end(); }); return; } response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end('okay'); }); const _websocketServer = new WebSocket.Server({ noServer: true }); _websocketServer.on('connection', handleConnection); _server.on('upgrade', (request, socket, head) => { // You may check auth of request here.. // See https://github.com/websockets/ws#client-authentication const protocol = request.headers['sec-websocket-protocol']; if (typeof protocol === 'string' && _tokens.get(protocol)) { _websocketServer.handleUpgrade(request, socket, head, ws => { _websocketServer.emit( 'connection', ws, request, _tokens.get(protocol) ); }); } else { socket.write('HTTP/1.1 401 Unauthorized'); socket.destroy(); } }); _server.listen(PORT, () => { console.log(`running at '${HOST}' on port ${PORT}`); });