From ddb54a28e2092aef46c4e57419784f30bbcaba01 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Mon, 29 May 2017 20:45:08 -0400 Subject: [PATCH] Add support for coturn authentication --- .gitignore | 1 + config/default.json | 6 ++++ package.json | 1 + src/client/__mocks__/iceServers.js | 1 + src/client/iceServers.js | 3 ++ src/client/peer/peers.js | 13 ++------ src/server/__tests__/turn-test.js | 53 ++++++++++++++++++++++++++++++ src/server/routes/call.js | 8 ++++- src/server/turn.js | 35 ++++++++++++++++++++ src/views/call.jade | 1 + 10 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 config/default.json create mode 100644 src/client/__mocks__/iceServers.js create mode 100644 src/client/iceServers.js create mode 100644 src/server/__tests__/turn-test.js create mode 100644 src/server/turn.js diff --git a/.gitignore b/.gitignore index 8e21771..0cef6f3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist/ node_modules/ config.js coverage/ +config/local.json diff --git a/config/default.json b/config/default.json new file mode 100644 index 0000000..be63c53 --- /dev/null +++ b/config/default.json @@ -0,0 +1,6 @@ +{ + "iceServers": [{ + "url": "stun:23.21.150.121", + "urls": "stun:23.21.150.121" + }] +} diff --git a/package.json b/package.json index 38c3bc7..c502b1d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "license": "MIT", "dependencies": { "bluebird": "^3.3.4", + "config": "^1.26.1", "express": "^4.13.3", "flux": "^2.1.1", "jade": "^1.11.0", diff --git a/src/client/__mocks__/iceServers.js b/src/client/__mocks__/iceServers.js new file mode 100644 index 0000000..e0a30c5 --- /dev/null +++ b/src/client/__mocks__/iceServers.js @@ -0,0 +1 @@ +module.exports = []; diff --git a/src/client/iceServers.js b/src/client/iceServers.js new file mode 100644 index 0000000..1588009 --- /dev/null +++ b/src/client/iceServers.js @@ -0,0 +1,3 @@ +module.exports = JSON.parse( + window.document.getElementById('iceServers').value +); diff --git a/src/client/peer/peers.js b/src/client/peer/peers.js index 073e9ad..f50d8ac 100644 --- a/src/client/peer/peers.js +++ b/src/client/peer/peers.js @@ -2,6 +2,7 @@ const _ = require('underscore'); const Peer = require('./Peer.js'); const debug = require('debug')('peer-calls:peer'); const dispatcher = require('../dispatcher/dispatcher.js'); +const iceServers = require('../iceServers.js'); const notify = require('../action/notify.js'); let peers = {}; @@ -25,17 +26,7 @@ function create({ socket, user, initiator, stream }) { let peer = peers[user.id] = Peer.init({ initiator: socket.id === initiator, stream, - config: { - iceServers: [{ - url: 'stun:23.21.150.121', - urls: 'stun:23.21.150.121' - }, { - url: 'turn:numb.viagenie.ca', - urls: 'turn:numb.viagenie.ca', - credential: 'muazkh', - username: 'webrtc@live.com' - }] - } + config: { iceServers } }); peer.once('error', err => { diff --git a/src/server/__tests__/turn-test.js b/src/server/__tests__/turn-test.js new file mode 100644 index 0000000..3497fd6 --- /dev/null +++ b/src/server/__tests__/turn-test.js @@ -0,0 +1,53 @@ +jest.unmock('../turn.js'); +const turn = require('../turn.js'); + +describe('turn', () => { + + describe('getCredentials', () => { + + it('returns username & credential', () => { + const auth = turn.getCredentials('a', 'b'); + expect(auth).toEqual(jasmine.any(Object)); + expect(auth.username).toEqual(jasmine.any(String)); + expect(auth.credential).toEqual(jasmine.any(String)); + }); + + }); + + describe('processServers', () => { + + const servers = [{ + url: 'server1', + urls: 'server1', + username: 'a', + credential: 'b' + }, { + url: 'server2', + urls: 'server2', + username: 'c', + secret: 'd', + auth: 'secret' + }] + + it('does not expose secret', () => { + const s = turn.processServers(servers); + expect(s.length).toBe(2); + expect(s[0]).toBe(servers[0]); + expect(s[1]).toEqual({ + url: 'server2', + urls: 'server2', + username: jasmine.any(String), + credential: jasmine.any(String) + }); + expect(s[1].username).toMatch(/^[0-9]+:c$/); + }) + + it('throws error when unknown auth type', () => { + expect(() => turn.processServers([{ auth: 'bla' }])) + .toThrowError(/not implemented/); + }); + + }); + +}); + diff --git a/src/server/routes/call.js b/src/server/routes/call.js index ee5e869..57f7698 100644 --- a/src/server/routes/call.js +++ b/src/server/routes/call.js @@ -1,8 +1,12 @@ #!/usr/bin/env node 'use strict'; +const config = require('config'); +const turn = require('../turn.js'); const router = require('express').Router(); const uuid = require('uuid'); +const cfgIceServers = config.get('iceServers'); + router.get('/', (req, res) => { let prefix = 'call/'; if (req.originalUrl.charAt(req.originalUrl.length - 1) === '/') prefix = ''; @@ -10,8 +14,10 @@ router.get('/', (req, res) => { }); router.get('/:callId', (req, res) => { + const iceServers = turn.processServers(cfgIceServers); res.render('call', { - callId: encodeURIComponent(req.params.callId) + callId: encodeURIComponent(req.params.callId), + iceServers }); }); diff --git a/src/server/turn.js b/src/server/turn.js new file mode 100644 index 0000000..d7395b0 --- /dev/null +++ b/src/server/turn.js @@ -0,0 +1,35 @@ +'use strict'; +const crypto = require('crypto'); + +function getCredentials(name, secret) { + // this credential would be valid for the next 24 hours + const timestamp = parseInt(Date.now() / 1000, 10) + 24 * 3600; + const username = [timestamp, name].join(':'); + const hmac = crypto.createHmac('sha1', secret); + hmac.setEncoding('base64'); + hmac.write(username); + hmac.end(); + const credential = hmac.read(); + return { username, credential }; +} + +function processServers(iceServers) { + return iceServers.map(server => { + switch (server.auth) { + case undefined: + return server; + case 'secret': + const cred = getCredentials(server.username, server.secret); + return { + url: server.url, + urls: server.urls, + username: cred.username, + credential: cred.credential + }; + default: + throw new Error('Authentication type not implemented: ' + server.auth); + } + }); +} + +module.exports = { getCredentials, processServers }; diff --git a/src/views/call.jade b/src/views/call.jade index c9da3a5..ffbeb07 100644 --- a/src/views/call.jade +++ b/src/views/call.jade @@ -12,6 +12,7 @@ html body.call input#callId(type="hidden" value="#{callId}") + input#iceServers(type="hidden" value="#{JSON.stringify(iceServers)}") div#container