Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby, Javascript and Elixir joining forces for WebRTC

Ben Langfeld
September 19, 2015

Ruby, Javascript and Elixir joining forces for WebRTC

Presentation delivered (in Portuguese, slides in English) at RubyConf Brasil 2015.

Ben Langfeld

September 19, 2015
Tweet

More Decks by Ben Langfeld

Other Decks in Programming

Transcript

  1. WebRTC Enables a web browser to access the camera and

    the microphone Enables a web browser to stream data (audio, video and arbitrary data) peer-to-peer
  2. WebRTC JavaScript Browser API for real-time communication (voice, video etc)

    Standards for interoperability Opus, G.711, VP8, H.264 SDP ICE/STUN/TURN DTLS-SRTP Mix of several related projects: WebRTC (W3C) - JavaScript APIs RTC-WEB (IETF) - Codecs, Security, Interop MMUSIC (IETF) - ICE (NAT traversal), SDP
  3. http: // Get m e Bob please! SDP :
 v=0

    o=alice 20518 0 IN IP4 0.0.0.0 s=- t=0 0 m =audio 54609 RTP/SAVPF 109 SDP :
 v=0 o=bob 19915 0 IN IP4 0.0.0.0 s=- t=0 0 m =audio 61001 RTP/SAV PF 109 Alice Bob SRTP SRTP
  4. WebRTC Support Client-Side Chrome Firefox Opera IE & Safari (with

    plugins for now) Server-Side FreeSWITCH (1.4+) Asterisk (11+) Many many more, including commercial Roll your own
  5. talkingstick.io The Talking Stick is a tradition among Native American

    (and other) cultures which specifies a simple yet effective way for facilitating group conversations.
  6. talkingstick.io Provide easy group-based (2 or more participants) audio/video communication

    Pluggable signalling delivery technology Plug & Play functionality - works out of the box A demonstration / WIP. Not a finished product.
  7. talkingstick.io gem ‘talking_stick' mount TalkingStick::Engine, at: ‘/talking_stick' rake railties:install:migrations db:migrate

    //= require talking_stick/application *= require talking_stick/application rails generate talking_stick:views http://localhost:3000/talking_stick/rooms
  8. Why add Elixir? Signalling w/ SIP (standard protocol) Scalability and

    concurrency (see other Elixir presentations) Separation of concerns & specialised tools (see Brandon Hilkert up next)
  9. SIP RFC3261 UDP / TCP / WebSocket Session Initiation Protocol

    RFC5411 - A Hitchhiker's Guide to the Session Initiation Protocol (SIP) Offer/Answer Sessions Registration Scalability Interoperability Presence Capabilities IM
  10. REGISTER sip:router SIP/2.0 Via: SIP/2.0/WS m64ov9h2vgc4.invalid;branch=z9hG4bK274469 Max-Forwards: 70 To: <sip:agent1@router>

    From: <sip:agent1@router>;tag=r1gf9eil91 Call-ID: 62i7d7sdejfcb6a4beuun5 CSeq: 81 REGISTER Contact: <sip:[email protected];transport=ws>;reg- id=1;+sip.instance="<urn:uuid:2c342cfb-9581-4a45-abc5- be8a126ae07a>";expires=600 Allow: ACK,CANCEL,BYE,OPTIONS,INFO,NOTIFY,INVITE Supported: path, gruu, 100rel, outbound User-Agent: SIP.js/0.7.1 Content-Length: 0
  11. SIP/2.0 200 OK Via: SIP/2.0/WS fsfuns3i609q.invalid;rport=60095;received=127.0.0.1;branch=z9hG4bK821 4320 From: <sip:agent1@router>;tag=e63j70ouke To:

    <sip:agent1@router>;tag=adk6yf Call-ID: pj2bg723k8aiakjoquc89c CSeq: 82 REGISTER Max-Forwards: 70 Content-Length: 0 Contact: <sip:[email protected];transport=ws>;reg- id=1;+sip.instance="<urn:uuid:2c342cfb-9581-4a45-abc5- be8a126ae07a>";expires=600 Require: outbound Supported: path,100rel,gruu,outbound,timer Allow: INVITE,ACK,CANCEL,BYE,OPTIONS,INFO,UPDATE,SUBSCRIBE,NOTIFY,REFER,MESS AGE,REGISTER,PRACK Date: Thu, 17 Sep 2015 21:14:04 GMT
  12. INVITE sip:agent1@router SIP/2.0 Via: SIP/2.0/WS g4nhn9dfbssc.invalid;branch=z9hG4bK6539183 Max-Forwards: 70 To: <sip:agent1@router>

    From: <sip:[email protected]>;tag=u9o oa4s40n Call-ID: 838ldk1hvmm9s59u8lqo CSeq: 4886 INVITE Contact: <sip:[email protected];transport=ws; ob> Allow: ACK,CANCEL,BYE,OPTIONS,INFO,NOTIFY Content-Type: application/sdp Supported: 100rel, outbound User-Agent: SIP.js/0.7.1 Content-Length: 2121 [some stuff (SDP)]
  13. var ua = new SIP.UA({ wsServers: 'ws://localhost:5080', register: true, uri:

    'agent1@router', password: '1234' }); var audio = new Audio(); ua.on('invite', function (session) { session.accept({ media: { constraints: { audio: true, video: false }, render: { remote: audio } } }); });
  14. $(function () { var currentSession; $(‘#call').bind('click', function () { currentSession

    = ua.invite('agent1@router', { media: { constraints: { audio: true, video: false }, render: { remote: audio } } }); }); $('#hangup').bind('click', function () { currentSession.bye(); }); });
  15. NkSIP is an Erlang SIP framework or application server, which

    greatly facilitates the development of robust and scalable server-side SIP applications like proxy, registrar, redirect or outbound servers, B2BUAs, SBCs or load generators. NkSIP takes care of much of the SIP complexity, while allowing full access to requests and responses.
  16. defmodule Router do @domain "router" def start do {:ok, _}

    = :nksip.start(:router, __MODULE__, [], [ {:plugins, [ :nksip_registrar, :nksip_100rel, :nksip_gruu, :nksip_outbound ]}, {:transports, [{:ws, :all, 5080}]} ]) end end
  17. def sip_route(_scheme, "", @domain, _req, _call) do :process end def

    sip_route(_scheme, _user, @domain, req, _call) do {:ok, ruri} = :nksip_request.meta(:ruri, req) case :nksip_gruu.registrar_find(:router, ruri) do [] -> {:reply, :temporarily_unavailable} uriList -> {:proxy, uriList, [:record_route]} end end def sip_route(_scheme, _user, _domain, req, _call) do case :nksip_request.is_local_ruri(req) do true -> :process false -> {:proxy, :ruri, [:record_route]} end end
  18. def sip_authorize(auth, req, _call) do {:ok, method} = :nksip_request.method(req) case

    method do :REGISTER -> case :lists.member(:dialog, auth) || :lists.member(:register, auth) do true -> :ok false -> case :nksip_lib.get_value({:digest, @domain}, auth) do true -> :ok # Password is valid false -> :forbidden # User has failed authentication :undefined -> {:proxy_authenticate, @domain} end end _ -> :ok end end def sip_get_user_pass(_user, @domain, _req, _call) do "1234" end
  19. Repeat submission of credentials by user. Shared credentials database. Permanent

    credentials exposed to authenticated user. PKI / X.509 client certificates.
  20. MojoAuth Based on HMAC (RFC2104) Derived from ideas presented in

    draft-uberti-behave-turn-rest by Justin Uberti, Google’s WebRTC lead coturn
  21. Client App Server Third- party Service 1.Client establishes session with

    App Server 2.Client requests temporary credentials from App Server 3.App Server provides client with temporary credentials 4.Client uses temporary credentials to connect with Third-party Service 5.Third-party service confirms the credentials and allows the connection 1. 2. 3. 4. 5.
  22. APP SERVER secret = “somesupersecret" expiry = (Time.now + 1.hour).to_i

    username = “#{expiry}:#{current_user.username}" password = Base64.encode64( OpenSSL::HMAC.digest('sha1', secret, username) ).chomp return { username: username, password: password }
  23. THIRD-PARTY SERVICE secret = "somesupersecret" expiry_timestamp, id = credentials[:username].split(':') return

    false if expiry_timestamp.to_i < Time.now.to_i password = Base64.encode64( OpenSSL::HMAC.digest( 'sha1', secret, credentials[:username] ) ).chomp return false unless password == credentials[:password] id || true
  24. APP SERVER THIRD-PARTY SERVICE secret = “somesupersecret" credentials = MojoAuth.create_credentials(

    id: 'foobar', secret: secret ) # => { username: ”1411837760:foobar”, password: ”wb6KxLj6NXcUaqNb1SlHH1V3QHw=“ } secret = "somesupersecret" # Test credentials MojoAuth.test_credentials( { username: “1411837760:foobar", password: “wb6KxLj6NXcUaqNb1SlHH1V3QHw=" }, secret: secret ) # => "foobar"
  25. WRONG SECRET secret = "theydontknowthesecret" # Test credentials MojoAuth.test_credentials( {

    username: “1411837760:foobar", password: “wb6KxLj6NXcUaqNb1SlHH1V3QHw=" }, secret: secret ) # => false
  26. IMPERSONATING OTHER USER secret = "somesupersecret" # Test credentials MojoAuth.test_credentials(

    { username: “1411837760:theadmin", password: “wb6KxLj6NXcUaqNb1SlHH1V3QHw=" }, secret: secret ) # => false
  27. AFTER EXPIRY secret = "somesupersecret" # Test credentials MojoAuth.test_credentials( {

    username: “1411837760:foobar", password: “wb6KxLj6NXcUaqNb1SlHH1V3QHw=" }, secret: secret ) # => false
  28. EXTENDING EXPIRY secret = "somesupersecret" # Test credentials MojoAuth.test_credentials( {

    username: “1441837760:foobar", password: “wb6KxLj6NXcUaqNb1SlHH1V3QHw=" }, secret: secret ) # => false
  29. Takeaways Standards are not evil Specialist tools for specialist tasks

    - no silver bullet Thinking out of the box can provide simple solutions Your app can have realtime communications very easily