
Lucjan Suski
aka What's going on at Squerb
 
        Lucjan Suski
Responsibilities:
Stack:
 
        Lucjan Suski
 
        Lucjan Suski
Each time user loads the page, we have to wait for the iframe to access the token
 
        Lucjan Suski
Push token after login using iframes / postMessage instead of pulling it.
Does not work on iOS due to very strict privacy policy - it doesn't allow to modify localStorage in iframe on another domain
 
        Lucjan Suski
Push token after login using redirection chain
Outage of one authenticatable service means breaking the redirection chain
 
        Lucjan Suski
 
        Lucjan Suski
It's very similar to previous one, the only difference is that we are POSTing data with email/password at the beginning, instead of just calling omniauth endpoint.
 
        Lucjan Suski
 
        Lucjan Suski
 
        Lucjan Suski
  def callback
    user = Authenticator.new(auth_hash).authenticate
    @token = fetch_auth_token(user)
    @domains = ENV['SYNCED_DOMAINS'].split(' ')
  end
<form action="<%= @domains[0] %>/auth_proxy" method="POST">
  <input type="hidden" value='<%= @domains[1..-1].to_json.html_safe %>' name="domains" />
  <input type="hidden" value="<%= @token %>" name="token" />
</form>
<script>
  document.getElementsByTagName('form')[0].submit()
</script>
http://auth.squerb.com/auth/facebook/callback
 
        Lucjan Suski
@AuthProxy =
  call: (options) ->
    @updateToken(options.token)
    if options.domains.length
      @redirectTo(options.domains, options.token)
    else
      window.close()
  updateToken: (token) ->
    localStorage.setItem('squerb_auth_token', token)
  redirectTo: (domains, token) ->
    form = createForm(action: "#{domains[0]}/auth_proxy", token: token, domains: domains[1..-1])
    form.submit()Squerb Auth Proxy gem
 
        Lucjan Suski
React login component
  pollToken: =>
    @lastToken = @get() || Date.now()
    setInterval =>
      if @get() != @lastToken
        @lastToken = @get()
        @trigger 'update'
        @onChange()
    , 1000@PopupLogin = React.createClass
  render: ->
    <Popup className="popup-login" title="Sign in to Squerb" ref="popup">
      { @props.children }
      <div>
        <SquerbAuth.Login token=CurrentUser.token onSuccess={@_closePopup} />
      </div>
    </Popup>
  _closePopup: ->
    @refs.popup._hide() 
        Lucjan Suski
(and how to deal with postMessage and CORS)
 
        Lucjan Suski
Take any client's page, put short JavaScript snippet and then... magic happens.
<script src="http://squerb-widgets-staging.herokuapp.com/voting.js"></script>
<script>
  $(document).ready(function(){
    widget = SquerbWidgets.voting({ 
        draggable: '.gallery-icon', 
        category: 'product', 
        title: 'What iPhone 6 will you buy, or not?', poll_id: 1 })
  })
</script> 
        Lucjan Suski
How does it look like?
 
        Lucjan Suski
 
        Lucjan Suski
Motivations:
 
        Lucjan Suski
class @XDMStream
  constructor: (options = {}) ->
    @stream = _.extend({}, Events)
    @socket = new XDMSocket
      onMessage: @onMessage
      remote: options.remote
      channel: options.channel
      whitelist: options.whitelist
  on: (event, callback) ->
    @stream.on(event, callback)
  off: (event, callback) ->
    @stream.off(event)
  trigger: (name, data) ->
    @socket.send({ name, data })
  onMessage: (message) =>
    @stream.trigger(message.name, message.data)
 
        Lucjan Suski
# domainA/some_endpoint
testStream = new XDMStream(channel: 'test', remote: 'domainB/some_endpoint')
testStream.on('another-message', -> console.log('message received on domainA'))
# at some point
testStream.trigger('test-message')
# domainB/some_endpoint
testStream = new XDMStream(channel: 'test', whitelist: 'domainA')
testStream.on('test-message', -> console.log('message received on domainB!'))
# at some point
testStream.trigger('another-message') 
        Lucjan Suski
class @XDMRpc
  constructor: (options = {}) ->
    @stream = new XDMStream
      remote: options.remote
      channel: options.channel
      whitelist: options.whitelist
  call: (name, attrs...) ->
    d = D()
    @stream.on "#{name}_response_success", d.resolve
    @stream.on "#{name}_response_failure", d.reject
    @stream.trigger("#{name}_request", attrs)
    d.promise
  register: (name, fn) ->
    @stream.on "#{name}_request", (attrs) =>
      D.promisify(fn(attrs...)).then (value) =>
        @stream.trigger "#{name}_response_success", value
      , (value) =>
        @stream.trigger "#{name}_response_failure", value
 
        Lucjan Suski
# domainA/some_endpoint
rpc = new XDMRpc(channel: 'test', whitelist: ['domainB'])
rpc.register('someMethod', -> console.log('some method called'))
# domainB/some_endpoint
rpc = new XDMRpc(channel: 'test', remote: 'domainA/some_endpoint')
rpc.call('someMethod') # will print 'some method called' on domainA 
        Lucjan Suski
 
        Lucjan Suski
 
        Lucjan Suski
# routes.rb
mount XdomainRails::Engine, at: '/xdomain'# config/initializers/xdomain-rails.rb
XdomainRails.configure do |config|
  config.master_domains = http:/master.example.com
end 
        Lucjan Suski
# application.html.erb, before any javascript file
<%= xdomain_slaves %># config/initializers/xdomain-rails.rb
XdomainRails.configure do |config|
  config.slave_domains = http://slave.example.com
end$.get('http://slave.example.com/secret.json') 
        Lucjan Suski
Questions?