Skip to content

Commit

Permalink
Added authentication needed for presence channel
Browse files Browse the repository at this point in the history
  • Loading branch information
James committed Aug 22, 2011
1 parent b6b70dd commit 152af2d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
6 changes: 5 additions & 1 deletion README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ The application will pause at socket.connect and handle events from Pusher as th

require 'pusher-client'
PusherClient.logger = Logger.new(STDOUT)
socket = PusherClient::Socket.new(YOUR_APPLICATION_KEY)
options = {:secret => 'YOUR_APPLICATION_SECRET'}
socket = PusherClient::Socket.new(YOUR_APPLICATION_KEY, options)

# Subscribe to two channels
socket.subscribe('channel1')
socket.subscribe('channel2')

# Subscribe to private/presence channel
socket.subscribe('presence-channel3', USER_ID)

# Bind to a global event (can occur on either channel1 or channel2)
socket.bind('globalevent') do |data|
puts data
Expand Down
59 changes: 51 additions & 8 deletions lib/pusher-client/socket.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require 'json'
require 'hmac-sha2'
require 'digest/md5'

module PusherClient
class Socket
Expand All @@ -10,12 +12,12 @@ class Socket
attr_accessor :encrypted, :secure
attr_reader :path, :connected, :channels, :global_channel, :socket_id

def initialize(application_key, options={})

def initialize(application_key, options={}, application_secret = nil)
raise ArgumentError if (!application_key.is_a?(String) || application_key.size < 1)

@path = "/app/#{application_key}?client=#{CLIENT_ID}&version=#{VERSION}"
@key = application_key
@secret = options[:secret]
@socket_id = nil
@channels = Channels.new
@global_channel = Channel.new('pusher_global_channel')
Expand All @@ -25,8 +27,9 @@ def initialize(application_key, options={})
@encrypted = options[:encrypted] || false

bind('pusher:connection_established') do |data|
socket = JSON.parse(data)
@connected = true
@socket_id = data['socket_id']
@socket_id = socket['socket_id']
subscribe_all
end

Expand Down Expand Up @@ -77,13 +80,12 @@ def disconnect
end
end

def subscribe(channel_name)
def subscribe(channel_name, user_id = nil)
@user_data = {:user_id => user_id}.to_json unless user_id.nil?

channel = @channels << channel_name
if @connected
send_event('pusher:subscribe', {
'channel' => channel.name
})
channel.acknowledge_subscription(nil)
authorize(channel, method(:authorize_callback))
end
return channel
end
Expand Down Expand Up @@ -117,6 +119,47 @@ def subscribe_all
}
end

#auth for private and presence
def authorize(channel, callback)
if is_private_channel(channel.name)
auth_data = get_private_auth(channel)
elsif is_presence_channel(channel.name)
auth_data = get_presence_auth(channel)
channel_data = @user_data
end
# could both be nil if didn't require auth
callback.call(channel, auth_data, channel_data)
end

def authorize_callback(channel, auth_data, channel_data)
send_event('pusher:subscribe', {
'channel' => channel.name,
'auth' => auth_data,
'channel_data' => channel_data
})
channel.acknowledge_subscription(nil)
end

def is_private_channel(channel_name)
channel_name.match(/^private-/)
end

def is_presence_channel(channel_name)
channel_name.match(/^presence-/)
end

def get_private_auth(channel)
string_to_sign = @socket_id + ":" + channel.name
HMAC::SHA256.hexdigest(@secret, string_to_sign)
end

def get_presence_auth(channel)
string_to_sign = @socket_id + ':' + channel.name + ':' + @user_data
signature = HMAC::SHA256.hexdigest(@secret, string_to_sign)
return "#{@key}:#{signature}"
end


# For compatibility with JavaScript client API
alias :subscribeAll :subscribe_all

Expand Down

0 comments on commit 152af2d

Please sign in to comment.