Pusher.channel_auth_endpoint = '/pusher_auth.php';
The default value for this is: /pusher/auth
Application security is very important so Pusher provides a mechanism for authenticating a user’s access to a channel at the point of subscription.
This can be used both to restrict access to private channels, and in the case of presence channels notify subscribers of who else is also subscribed via presence events.
The basic mechanism for doing this is broadly the same for both scenarios. The following sequence diagram shows the signing process:
Pusher object is created a new WebSocket object is created.Once the connection has been established a universally unique socket_id is returned to the Pusher JavaScript library.
private- or presence- channel.private- or presence- prefix identifies the channel as requiring authentication so a request is made to an authentication endpoint via either AJAX or JSONp. For more information see channel authentication transports.channel_name and authentication signature is sent to Pusher over the WebSocket, which completes the authorisation if the authorisation string has been correctly signed.For example, in both the subscribe calls below an authentication request will take place.
var pusher = new Pusher('APP_KEY');
var privateChannel = pusher.subscribe('private-channel');
var presenceChannel = pusher.subscribe('presence-channel');
The purpose of the authentication callback is so that you can check that the current user of your application has permission to access the channel that they are trying to subscribe to. How the user is authenticated depends on the decisions you make and the system you are developing.
The destination of the authentication request can be configured.
Pusher.channel_auth_endpoint = '/pusher_auth.php';
The default value for this is: /pusher/auth
In order to connect to a private or presence channel using libPusher, you first need to configure your server authorisation URL.
_client.authorizationURL = [NSURL URLWithString:@"http://www.yourserver.com/authorise"];
When you attempt to connect to a private or presence channel, libPusher will make a form-encoded POST request to the above URL, passing along the socket_id and channel_name as parameters. Prior to sending the request, the Pusher delegate will be notified, passing in the NSMutableURLRequest instance that will be sent.
Its up to you to configure the request to handle whatever authentication mechanism you are using. In this example, we simply set a custom header with a token which the server will use to authenticate the user before proceeding with authorisation.
- (void)pusher:(PTPusher *)pusher willAuthorizeChannelWithRequest:(NSMutableURLRequest *)request
{
[request setValue:@"some-authentication-token" forHTTPHeaderField:@"X-MyCustom-AuthTokenHeader"];
}
Channel authentication transport is only applicable to the Pusher JavaScript library.
The authentication transport can be configured by setting a static property on the Pusher object.
Pusher.channel_auth_transport = 'jsonp';
The default value for this is: ajax.
The HTTP POST request that is made to the authentication endpoint when a subscription takes place contains the following request parameters:
socket_id
channel_name
JSONp authentication can be used when the authentication endpoint is on a different domain to the web application. An additional parameter called callback will also be passed identifying the JavaScript function to be called from the script generated by the authentication endpoint.
Note: To use JSONp support, you must use Pusher’s client version 1.6 or above.
As described above you need to set the Pusher.channel_auth_transport to jsonp and optionally define a value for Pusher.channel_auth_endpoint.
When using JSONp the authentication the following parameters will be passed as query parameters (a GET request):
socket_id
channel_name
callback
As discussed, the duty of the server is to check the user has permission to subscribe to the supplied channel. If the user does have permission then an authentication HTTP response must be returned in a JSON format with an authentication signature. We have a number of server libraries that make doing this really simple.
Note: If you don’t want to use one of the existing libraries, or there isn’t one in the technology you want to use, please see authenticating signatures or get in touch.
The following sections show how this is achieved for the different types of channels.
Here are some examples of private channel authentication for different languages:
class PusherController < ApplicationController
protect_from_forgery :except => :auth # stop rails CSRF protection for this action
def auth
if current_user
response = Pusher[params[:channel_name]].authenticate(params[:socket_id])
render :json => response
else
render :text => "Forbidden", :status => '403'
end
end
end
global $user;
if ($user->uid)
{
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
echo $pusher->socket_auth($_POST['channel_name'], $_POST['socket_id']);
}
else
{
header('', true, 403);
echo "Forbidden";
}
if ( is_user_logged_in() )
{
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
echo $pusher->socket_auth($_POST['channel_name'], $_POST['socket_id']);
}
else
{
header('', true, 403);
echo "Forbidden";
}
var express = require( 'express' );
var Pusher = require( 'pusher' );
var app = express( express.logger() );
app.use( express.bodyParser() );
var pusher = new Pusher( { appId: APP_ID, key: APP_KEY, secret: APP_SECRET } );
app.post( '/pusher/auth', function( req, res ) {
var socketId = req.body.socket_id;
var channel = req.body.channel_name;
var auth = pusher.auth( socketId, channel );
res.send( auth );
} );
var port = process.env.PORT || 5000;
app.listen( port );
using PusherServer;
public class MyController : Controller
{
public ActionResult Auth(string channel_name, string socket_id)
{
var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET);
var auth = pusher.Authenticate( channel_name, socketId );
var json = auth.ToJson();
return new ContentResult { Content = json, ContentType = "application/json" };
}
}
class AuthHandler(webapp.RequestHandler):
def post(self):
channel_name = self.request.get('channel_name')
socket_id = self.request.get('socket_id')
p = pusher.Pusher(app_id=APP_ID, key=APP_KEY, secret=APP_SECRET)
auth = p[channel_name].authenticate(socket_id)
json_data = json.dumps(auth)
self.response.out.write(json_data)
def main():
application = webapp.WSGIApplication([('/pusher/auth', AuthHandler)])
The generated JSON should look as follows:
{
"auth":"278d425bdf160c739803:a99e78e7cd40dcd0d4ae06be0a5395b6cd3c085764229fd40b39ce92c39af33e"
}
Authentication of a presence channel is performed in exactly the same way as a private channel but the JSON response must have a channel_data property containing information that you wish to share about the current user.
Here are some examples of presence channel authentication for different languages:
class PusherController < ApplicationController
protect_from_forgery :except => :auth # stop rails CSRF protection for this action
def auth
if current_user
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {
:user_id => current_user.id, # => required
:user_info => { # => optional - for example
:name => current_user.name,
:email => current_user.email
}
})
render :json => response
else
render :text => "Forbidden", :status => '403'
end
end
end
global $user;
if ($user->uid)
{
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
$presence_data = array('name' => $user->name);
echo $pusher->presence_auth($_POST['channel_name'], $_POST['socket_id'], $user->uid, $presence_data);
}
else
{
header('', true, 403);
echo( "Forbidden" );
}
if ( is_user_logged_in() )
{
global $current_user;
get_currentuserinfo();
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
$presence_data = array('name' => $current_user->display_name);
echo $pusher->presence_auth($_POST['channel_name'], $_POST['socket_id'], $current_user->ID, $presence_data);
}
else
{
header('', true, 403);
echo( "Forbidden" );
}
using PusherServer;
public class MyController : Controller
{
public ActionResult Auth(string channel_name, string socket_id)
{
var pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET);
var channelData = new PresenceChannelData() {
user_id: "unique_user_id",
user_info: new {
name = "Mr Pusher",
twitter_id = "@pusher"
}
};
var auth = pusher.Authenticate( channelName, socketId, channelData );
var json = auth.ToJson();
return new ContentResult { Content = json, ContentType = "application/json" };
}
}
var express = require( 'express' );
var Pusher = require( 'pusher' );
var app = express( express.logger() );
app.use( express.bodyParser() );
var pusher = new Pusher( { appId: APP_ID, key: APP_KEY, secret: APP_SECRET } );
app.post( '/pusher/auth', function( req, res ) {
var socketId = req.body.socket_id;
var channel = req.body.channel_name;
var presenceData = {
user_id: 'unique_user_id',
user_info: {
name: 'Mr Pusher',
twitter_id: '@pusher'
}
};
var auth = pusher.auth( socketId, channel, presenceData );
res.send( auth );
} );
var port = process.env.PORT || 5000;
app.listen( port );
class AuthHandler(webapp.RequestHandler):
def post(self):
channel_name = self.request.get('channel_name')
socket_id = self.request.get('socket_id')
channel_data = {'user_id': socket_id}
channel_data['user_info'] = {'name':'Test Name'}
p = pusher.Pusher(app_id=APP_ID, key=APP_KEY, secret=APP_SECRET)
auth = p[channel_name].authenticate(socket_id, channel_data)
json_data = json.dumps(auth)
self.response.out.write(json_data
def main():
application = webapp.WSGIApplication([('/pusher/auth', AuthHandler)])
The generated JSON should look similar to the private channel JSON with an additional channel_data property which should be valid JSON encoded as a string:
{
"auth":"49e26cb8e9dde3dfc009:a8cf1d3deefbb1bdc6a9d1547640d49d94b4b512320e2597c257a740edd1788f",
"channel_data":"{\"user_id\":\"Phil Leggetter\",\"user_info\":{\"name\":\"Phil Leggetter\",\"imageUrl\":\"http:\\\/\\\/www.gravatar.com\\\/avatar\\\/ecc56977271e781991b6172c16248459?s=80&d=mm&r=g\"}}"
}
JSONp auth endpoints are only applicable to the Pusher JavaScript library.
As discussed above the channel_auth_transport should be set to jsonp in order to use JSONp authentication. If JSONp authentication is being used then it is highly likely that the endpoint is on a different domain so the channel_auth_endpoint should also be set.
<script src="http://js.pusher.com/2.1/pusher.min.js"></script>
<script>
Pusher.channel_auth_transport = 'jsonp';
Pusher.channel_auth_endpoint = 'http://myserver.com/pusher_jsonp_auth';
</script>
Then you would subscribe to a private or presence channel, as needed.
<script>
var pusher = new Pusher('MY_PUSHER_KEY');
var channel = pusher.subscribe('private-test_channel');
channel.bind('greet', function(data) {
alert(data.greeting);
});
</script>
As demonstrated in the sequence diagram the server would then receive a call. Authentication works in much the same way as it does with an AJAX request. However, the request parameters are passed in the query string, and the authentication response must be a string which represents a JavaScript function call which indicates the authentication response. The function to be called is identified by the callback parameter in the query string.
The following examples demonstrate how private channels are authenticated and how the response is wrapped in a callback. Presence channels are authenticated as demonstrated in the Presence endpoint section and the callbacks wrapped in exactly the same way as shown here.
class PusherController < ApplicationController
protect_from_forgery :except => :auth # stop rails CSRF protection for this action
def auth
if current_user
auth = Pusher[params[:channel_name]].authenticate(params[:socket_id])
render :text => params[:callback] + "(" + auth.to_json + ")"
else
render :text => "Forbidden", :status => '403'
end
end
end
global $user;
if ($user->uid)
{
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
$auth = $pusher->socket_auth($_GET['channel_name'], $_GET['socket_id']);
$callback = str_replace('\\', '', $_GET['callback']);
echo($callback . '(' . $auth . ');');
}
else
{
header('', true, 403);
echo "Forbidden";
}
if ( is_user_logged_in() )
{
$pusher = new Pusher(APP_KEY, APP_SECRET, APP_ID);
$auth = $pusher->socket_auth($_GET['channel_name'], $_GET['socket_id']);
$callback = str_replace('\\', '', $_GET['callback']);
echo($callback . '(' . $auth . ');');
}
else
{
header('', true, 403);
echo "Forbidden";
}