
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?