Pusher Private Channel Auth info required to subscribe to private-asdf - php

Trying to subscribe to a private channel with Pusher. But every time it tries to subscribe I get the error below. Which makes no sense since it's clearly returning the auth info.
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Auth info required to subscribe to private-sadfsadf"}}}
Normally it's returning the auth json (below) with status 200.
"{\"auth\":\"0b1ce844906bd4d82cb4:21571e5667bf99f17bbf67ae0411594560748fde30b9edeca653653158f8a1f5\"}"
Pusher PHP (shortened)
$pusher = new Pusher($app_key, $app_secret, $app_id);
$auth = $pusher->socket_auth($postvars['channel_name'], $postvars['socked_id']);
if ($auth)
return $response->withJSON($auth);
else
return $response->withStatus(403);
Pusher JS
var pusher = new Pusher('0b1ce844906bd4d82cb4', {
cluster: 'us2',
encrypted: true,
authEndpoint: '{{site.uri.public}}/chat/auth/{{game.id}}',
authTransport: 'ajax',
auth: {
params: {
'csrf_name': '{{site.csrf.name}}',
'csrf_value': '{{site.csrf.value}}'
},
headers: {
'{{site.csrf.name}}': '{{site.csrf.value}}'
}
}
});

I had this error and it was caused by having the BROADCAST_DRIVER set to redis instead of pusher.

it was because my endpoint /pusher/auth returned the signature in JSON format.
So, I just changed this:
return $response
To this:
return json_decode($response)
NB: PUSHER signature use hash_hmac to generate hash:
you can compare you entryPoint return with
$sig = hash_hmac('sha256',$socket, $channelNam);
var_dump($sig);

Related

Laravel Echo + Pusher subscription_error on private channel

I can't manage to get echo and pusher working when using private channels, googled for two days and found nada.
What seems to be happening is some sort of problem with the authentication (I'm using Laravel basic Auth) cause I can subscribe to public channels
routes/channels.php
Broadcast::channel('private-ci-received-{userId}', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
Broadcast::channel('private-ci-received-{toUserId}', function ($currentUser, $toUserId) {
return true;
});
bootstrap.js
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
logToConsole: true,
encrypted: true,
});
default.blade.php(main layout)
Pusher.logToConsole = true;
Echo.logToConsole = true;
Echo.private('ci-received-{{ Auth::user()->id}}')
.listen('.CIReceived', (e) => {
console.log(e);
});
What get printed on the console is :
Pusher : No callbacks on private-ci-received-1 for pusher:subscription_error
It's a pretty generic error, then for debug purposes I tried to bind the error using Pusher (not laravel echo)
var pusher = new Pusher('MYSECRETAPPKEYHERE', {
cluster: 'us2',
forceTLS: true
});
var channel = pusher.subscribe('private-ci-received-1');
channel.bind('pusher:subscription_error', function(data) {
console.log(data);
});
console.log(data) output
JSON returned from webapp was invalid, yet status code was 200
The default authEndPoint is /broadcasting/auth IIRC I think it expects to return a JSON but instead it returns the HTML CODE from my page.
Those routes are created by the framework itself and from what I've read Laravel echo and Laravel Auth should work great together without much fiddling.
My .env file is correct i'm using pusher as broadcast driver and BroadcastServiceProvider is properly uncommented.
Can anyone shed a light in the matter? Thanks
This worked for me
Default value of .env is
BROADCAST_DRIVER=log
Pls change it to
BROADCAST_DRIVER=pusher
I was also getting ["No callbacks on private-user.1 for pusher:subscription_error"]
After making the above changes, it works fine for me
Set APP_DEBUG=true in your .env file and check the HTTP response of your auth route in the network section of your browser's developer tools. If authentication is successful, your laravel app should respond with JSON like this:
{"auth": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
If there's an error it will show you in the response since you set debug mode to true.
You do not need to add private- onto your channel in broadcasting.php this is done automagically
Try this
Broadcast::channel('ci-received-{userId}', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});

Authorization for laravel passport through socket.io for broadcasting channels

I'm using laravel 5.3 + passport for authorization, Laravel is my back-end API which is restful.
front-end is written in angular.js which communicate with API with rest requests.
For Real-time notifications, I've used laravel broadcasting events + redis, and socket.io for socket server and socket client in angular.js.
I want to authorize these events and I've done it far as I could :
BroadcastServiceProvider :
public function boot()
{
Broadcast::routes(['middleware' => ['auth:api']]);
Broadcast::channel('App.User.*', function ($user, $userId)
{
return (int) $user->id === (int) $userId;
});
Broadcast::channel('notifs.*', function ($user, $userId) {
return $user->id === (int) $userId;
});
}
This is my socket.js code which runs my socket server :
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.psubscribe('*', function(err, count) {});
redis.on('pmessage', function(subscribed, channel, message) {
console.log(channel);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function () {
console.log('Listening on Port 3000');
});
redis.on("error", function (err) {
console.log(err);
});
The problem is I don't know how to authenticate these broadcasting events in socket server and also how to authorize the user in angular.js (SPA) to listen to these events.
I'd appreciate any help.
I'd definitely take a look at socketio-auth.
This module provides hooks to implement authentication in socket.io
without using querystrings to send credentials, which is not a good
security practice.
Another approach I took recently was simple token based authentication using JWT tokens (njwt).
I did not want to recreate the authentication code that checks user credentials within Node.js. (which in my case can't even connect to the database anyway). Rather, I'd let the PHP application that was using the socket leverage its already established authentication system. Passing a signed token with the socket connect requests.
Your node.JS code might look something like...
primus.on('connection', function (spark) {
logger.debug('primus event connection. spark id: ' + spark.id);
spark.on('data', function(data) {
var action = data.action;
njwt.verify(data.token, JWT_SECRET, function(err, verifiedJwt) {
if (err) {
logger.warn('Bad JWT Token! ' + spark.id + ' Error: ' + err);
spark.user = {id:null, is_authed: false, is_admin: false, application_ini: null};
spark.end('Bad Token Request');
return; //->
}
spark.user = { 'id': verifiedJwt.body['user_id'],
'is_authed': verifiedJwt.body['is_authed'],
'application_ini': verifiedJwt.body['application_ini'],
'is_admin': verifiedJwt.body['is_admin']};
sockoasRooms.connect(spark.id, spark.user.application_ini, spark.user.id);
switch (action) {
...
And then on the PHP side, you'll need some code for generating the JWT tokens, but use is very simple. Something like:
<?php
$tokenPayload = [ 'user_id' => ($this->currentUser) ? $this->currentUser->getId() : 0,
'is_authed' => ($this->currentUser) ? true : false,
'application_ini' => (string) APPLICATION_INI,
'is_admin' => (bool) ($this->currentUser) ? $this->currentUser->isAdministrator() : false,
'now' => time()
];
$jwtToken = \OAS_JWT::encode($tokenPayload, SOCK_OAS_JWT_KEY);
?>
$(document).ready(function() {
primus = Primus.connect('ws://<?=SOCK_OAS_IP?>:<?=SOCK_OAS_PORT?>/_admin');
primus.on('open', function () {
showConnected();
// Send request to subscribe
primus.write({action: 'dashboard-dump', token: '<?=$jwtToken?>'});
consoleWrite('Connected to dashboard.');
});
You can evaluate the time component to avoid replay attacks. Anyway, sounds like this approach might meet your needs.
Off topic but I'd also suggest taking a look at primus. It acts as a "universal wrapper for real-time frameworks". This lets you abstract things in a way so you could swap out the socket libraries with no hassle. Might be a little lower level (engine.IO) than what you are using though.

Magento 2 REST API

I' m trying to create a node.js client for the Magento REST API.
I use the community edition running on my own centOS server.
The steps I follow to use the api:
Create user with Administrator role in Magento admin panel
Create new Integration
Activate Integration (after this, I receive ConsumerKey, ConsumerSecret, AccessToken, SecretToken)
The problem is: when I try a get request to any endpoint I get:
{
Client is not authorized to access this resource: ResourceName
}
the request is:
request({
url: this.url+endpoint,
method: 'GET',
headers:{
'Authorization': 'Bearer '+bearer,
'User-Agent': '...',
'Accept' : 'application/json',
'Content-Type': 'application/json'
}
}, (error, response, body) => {
callback(error,response,body)
})
})
I correctly get the bearer ( I checked it ) token by this request:
request({
url: this.url+'integration/admin/token',
method: 'POST',
json:{
username: this.username,
password: this.password
}
}, (error, response, body) => {
callback(body)
})
Navigating through the magento installation, I ran into the authorization request:
public function isAllowed($resource, $privilege = null)
{
return $this->_aclPolicy->isAllowed($this->_aclRoleLocator->getAclRoleId(), $resource, $privilege);
}
If I change into
return true
I get access to all the resources without authentication, but it' s not what I want
I checked the function
getAclRoleId()
and I found this DefaultRoleLocator.php
namespace Magento\Framework\Authorization\RoleLocator;
class DefaultRoleLocator implements
\Magento\Framework\Authorization\RoleLocatorInterface
{
/**
* Retrieve current role
*
* #return string
*/
public function getAclRoleId()
{
return '';
}
}
I am not very good in PHP, and I' m a newbie on magento.
Even the oauth authentication fails.
I have to change this function ?
I have to create another RoleLocator class ?
Any help would be really appreciated !
Are you sure you added permissions to access the requested resource?

Pusher WebSocketError -> code:4200 - message:Please reconnect immediately

we are using Pusher as our notification system and it is causing problem sometimes.
The issue is sometimes the connection closes automatically and the following error is printed on the console (actually this is an older error I used to get on console until some weeks ago):
Pusher : Error :
{"type":"WebSocketError","error":{"type":"PusherError","data":{"code":1006}}}
And recently I see this one:
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":4200,"message":"Please reconnect immediately"}}}
Here is the client (JS) code:
function initPusher(user) {
if (pusher) {
return;
} else {
Pusher.logToConsole = false;
pusher = new Pusher('pusher key', {
cluster: 'eu',
encrypted: true
});
try {
var channel = pusher.subscribe(user.user_channel);
channel.bind('app_event', function(data) {
if (data['event_type'] === 'reply-message') {
$rootScope.$broadcast('REPLY_RECEIVED', data);
} else if (data['event_type'] === 'new-message') {
$rootScope.$broadcast('MESSAGE_RECEIVED', data);
}
});
} catch(err) {
$state.go('logout');
}
}
}
This function is called on login and every page refresh.
Although I guess this is a client side issue I add the relevant PHP code where the API is instantiated in index.php:
require_once __DIR__.'/../vendor/pusher/pusher-php-server/lib/Pusher.php';
$options = array(
'cluster' => 'eu',
'encrypted' => true
);
$pusher = new Pusher(
'pusher key',
'secret',
'app id,
$options
);
Further Details
Backend framework in use is Silex and the frontend one AngularJS.
We are using the EU cluster of Pusher and it works fine while the connection is still open.
I have already seen this link but couldn't find an answer to my problem there.

Laravel pusher error: JSON returned from webapp was invalid, yet status code was 200

i work on laravel 5.2 and i added vinkla/laravel-pusher to do real time chat app on my site but i faced this issue:
"JSON returned from webapp was invalid, yet status code was 200."
this my controller:
public function authMessageChat(Request $req)
{
$channelName = e($req->input('channel_name'));
$socketId = e($req->input('socket_id'));
$auth = $this->pusher->presence_auth($channelName, $socketId, auth()->user()->id);
return response($auth);
}
this is my script:
var pusher = new Pusher('{{env("PUSHER_KEY")}}', {
authEndpoint: '../auth',
auth: {
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
params: {
id: currentUser.id
}
}
});
var channel = pusher.subscribe('{{$chatChannel}}');
channel.bind('pusher:subscription_error', function(PusherError){
console.log('PusherError' + PusherError);
});
channel.bind('new-message', function(data) {
console.log(data.sender);
)};
Pusher error:
#chrismou Thank you for your answer..
your answer is right and we can implement the solution in another way..
just go to your .env file and make:
APP_DEBUG=false
instead of
APP_DEBUG=true
Thank you
It looks like your app is returning the debugbar html as part of the json response.
Assuming it's the barryvdh/laravel-debugbar you're using, according to the documentation you can switch off the debug bar at runtime:
Config::set('laravel-debugbar::config.enabled', false);
I assume you'll need to add that prior to the response being sent, in the controller method/route you're using for the authentication endpoint (ie, the one you're calling the socket_auth method from)

Categories