Cannot subscribe to a private or presence channel because no Authorizer has been set. Call PusherOptions.setAuthorizer() before connecting to Pusher - laravel

In a chat application,
Laravel 7.x,
Vuejs 2,
Flutter 2.8 and Pusher
Pacakges I use pusherjs, Laravel echo, pusher-php-server and Flutter pusher_client.
in Laravel & Vue public and private channels work fine, in with Flutter public channel also working but, subscribing to private channel show the following error.
Cannot subscribe to a private or presence channel because no Authorizer has been set. Call PusherOptions.setAuthorizer() before connecting to Pusher
W/System.err(10758): java.lang.IllegalStateException: Cannot subscribe to a private or presence channel because no Authorizer has been set. Call PusherOptions.setAuthorizer() before connecting to Pusher
W/System.err(10758): at com.pusher.client.Pusher.throwExceptionIfNoAuthorizerHasBeenSet(Pusher.java:376)
W/System.err(10758): at com.pusher.client.Pusher.subscribePrivate(Pusher.java:281)
W/System.err(10758): at com.github.chinloyal.pusher_client.pusher.PusherService.subscribe(PusherService.kt:152)
W/System.err(10758): at com.github.chinloyal.pusher_client.pusher.PusherService.access$subscribe(PusherService.kt:30)
W/System.err(10758): at com.github.chinloyal.pusher_client.pusher.PusherService$register$1.onMethodCall(PusherService.kt:56)
W/System.err(10758): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
W/System.err(10758): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:178)
W/System.err(10758): at io.flutter.embedding.engine.dart.DartMessenger.lambda$handleMessageFromDart$0$DartMessenger(DartMessenger.java:206)
W/System.err(10758): at io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$R4HPk6oFVb-i-YR_PN9YE6kqx1I.run(Unknown Source:12)
W/System.err(10758): at android.os.Handler.handleCallback(Handler.java:883)
W/System.err(10758): at android.os.Handler.dispatchMessage(Handler.java:100)
W/System.err(10758): at android.os.Looper.loop(Looper.java:237)
W/System.err(10758): at android.app.ActivityThread.main(ActivityThread.java:7948)
W/System.err(10758): at java.lang.reflect.Method.invoke(Native Method)
W/System.err(10758): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
W/System.err(10758): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
D/PusherClientPlugin(10758): Event stream cancelled.
D/PusherClientPlugin(10758): Event stream listening...
D/PusherClientPlugin(10758): [BIND] chat-event
E/flutter (10758): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(SUBSCRIBE_ERROR, Cannot subscribe to a private or presence
channel because no Authorizer has been set. Call PusherOptions.setAuthorizer() before connecting to Pusher, java.lang.IllegalStateException: Cannot subscribe to a private or presence channel because no Authorizer has been set. Call PusherOptions.setAuthorizer() before connecting to Pusher
E/flutter (10758): at com.pusher.client.Pusher.throwExceptionIfNoAuthorizerHasBeenSet(Pusher.java:376)
E/flutter (10758): at com.pusher.client.Pusher.subscribePrivate(Pusher.java:281)
E/flutter (10758): at com.github.chinloyal.pusher_client.pusher.PusherService.subscribe(PusherService.kt:152)
E/flutter (10758): at com.github.chinloyal.pusher_client.pusher.PusherService.access$subscribe(PusherService.kt:30)
E/flutter (10758): at com.github.chinloyal.pusher_client.pusher.PusherService$register$1.onMethodCall(PusherService.kt:56)
E/flutter (10758): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/flutter (10758): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:178)
E/flutter (10758): at io.flutter.embedding.engine.dart.DartMessenger.lambda$handleMessageFromDart$0$DartMessenger(DartMessenger.java:206)
E/flutter (10758): at io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$R4HPk6oFVb-i-YR_PN9YE6kqx1I.run(Unknown Source:12)
E/flutter (10758): at android.os.Handler.handleCallback(Handler.java:883)
E/flutter (10758): at android.os.Handler.dispatchMessage(Handler.java:100)
E/flutter (10758): at android.os.Looper.loop(Looper.java:237)
E/flutter (10758): at android.app.ActivityThread.main(ActivityThread.java:7948)
E/flutter (10758): at java.lang.reflect.Method.invoke(Native Method)
E/flutter (10758): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
E/flutter (10758): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
E/flutter (10758): , null)
E/flutter (10758): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (10758): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)
E/flutter (10758): <asynchronous suspension>
this is the function inside controller
public function newMessage(Request $request, $roomId)
{
$newMessage = new Message();
$newMessage->user_id = Auth::id();
$newMessage->room_id = $roomId;
$newMessage->message = $request->message;
$newMessage->save();
broadcast(new NewChatMessage($newMessage))->toOthers();
return response()->json($newMessage);
}
this is the event class
class NewChatMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public function __construct(Message $message)
{
$this->message = $message;
}
public function broadcastOn()
{
return new PrivateChannel('chat.'. $this->message->room_id);
}
public function broadcastAs()
{
return 'chat-event';
}
}
channels.php
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
if(Auth::check()){
return ['id'=> $user->id, 'name'=> $user->name ];
}
});
In Flutter I use flutter_client package the following code is copied from it's documentation
Future<void> _initPusher() async {
String token = await _store.read('token');
try {
pusher = PusherClient(
"6f8...c3",
PusherOptions(
host: '10.10.10.251',
encrypted: false,
auth: PusherAuth(
'http://10.10.10.251:8000/broadcasting/auth',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
),
cluster: 'eu',
),
enableLogging: true,
);
channel = pusher.subscribe("private-chat.2");
pusher.onConnectionStateChange((state) {
print(
"previousState: ${state.previousState}, currentState: ${state.currentState}");
});
pusher.onConnectionError((error) {
print("error: ${error.message}");
});
channel.bind('chat-event', (event) {
print(event.data);
});
} catch (e) {
print(e);
}
}
I can successfully subscribe to public channel by bellow code.
Future<void> _initPusher() async {
try {
pusher = PusherClient(
"6f8...c3",
PusherOptions(
encrypted: false,
cluster: 'eu',
),
enableLogging: true,
);
channel = pusher.subscribe("public-channel-name");
pusher.onConnectionStateChange((state) {
print(
"previousState: ${state.previousState}, currentState: ${state.currentState}");
});
pusher.onConnectionError((error) {
print("error: ${error.message}");
});
channel.bind('chat-event', (event) {
print(event.data);
});
} catch (e) {
print(e);
}
}
your help is really appreciated.

Give this a shot.
Android Pusher AuthorizationFailureException: FileNotFoundException
removing the
'Content-Type': 'application/json',
From your headers might do the trick.

#usman fawwaz, I solved using pusher_client in Flutter. If you want to subscribe to a private channel init pusher as follow.
Future<void> _initPusher() async {
String token = await Store.read('token');
try {
pusher = PusherClient(
pusherKey,
PusherOptions(
host: basePath,
encrypted: false,
auth: PusherAuth(
basePath + "/broadcasting/auth",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token'
},
),
cluster: 'eu',
),
enableLogging: true,
);
channel = pusher?.subscribe("private-chat." + widget.room.id.toString());
pusher?.onConnectionStateChange((state) {
print(
"previousState: ${state?.previousState}, currentState: ${state?.currentState}");
});
pusher?.onConnectionError((error) {
print("error: ${error?.message}");
});
channel?.bind('chat-event', (event) {
setState(() {});
});
} catch (e) {
print(e.toString());
}
}
for me the key point was; laravel by default has a route named "broadcasting/auth" I didn't know this.
Pusher itself made a package called pusher_channels_flutter: ^2.0.2 I want to switch to this package, here also I have subscription problem with private package.

Related

How to use private channel in Laravel Pusher.js

I use Pusher.js in Laravel.
Here is a private channel:
return new PrivateChannel('user.'.$this->user->id);
With permissions:
Broadcast::channel('user.{userId}', function ($user, $userId) {
return true;
});
The PrivateChannel has channel prefix name in constructor:
parent::__construct('private-'.$name);
Therefore I use private- prefix in JS client:
var channel = pusher.subscribe('private-user.1');
channel.bind('PrivateEvent', function(data) {
console.log(data);
});
The problem is I got client error, because the private channel awaits auntification:
Pusher : : ["Error: Unable to retrieve auth string from channel-authorization endpoint - received status: 404 from /pusher/auth. Clients must be authenticated to join private or presence channels.
Why should I use auntification twice if Laravel already checks this in route channel?
You need first to authorize your request by adding the authEndpoint and add the jwt_token like this after that you could listen to the channel
var pusher = new Pusher("PUBLIC_KEY", {
cluster: 'eu',
authEndpoint: `https://domain_name.com/broadcasting/auth`,
auth: {
headers: {
"Authorization": "Bearer YOUR_JWT_TOKEN",
"Access-Control-Allow-Origin": "*"
}
}
});
var channel = pusher.subscribe('private-user.1');
channel.bind('PrivateEvent', function(data) {
console.log(data);
});

Logging in and calling normal API Routes does not work with websockets

I have an issue with the authentication process of a websocket route.
I'm using PassportJS with 'local' strategy.
Logging in and calling normal API Routes are perfectly working with #AuthenticatedGuard,
but not on websockets
On the Websocket Route, it is throwing following error:
ERROR [WsExceptionsHandler] request.isAuthenticated is not a function
TypeError: request.isAuthenticated is not a function
This is my setup:
Example normal working API Route
test.controller.ts
#UseGuards(AuthenticatedGuard)
#Get(':id')
async findOne(
#Param('id') id: string,
#Req() req: any,
#Res() res: Response,
) {
return res.json(
await this.testService.findOne(+id),
);
}
Websocket Route
messages.gateway.ts
#UseGuards(AuthenticatedGuard)
#SubscribeMessage('findAllMessages')
async findAll(
#ConnectedSocket() client: Socket,
#MessageBody() findAllMessages: findAllMessages,
#Req() req: any,
) {
console.log(req);
//return this.messagesService.findAll(chatroomid);
const user = await this.messagesService.checkCurrentSessionString(
req.handshake.headers.cookie,
);
if (req.user.userid != undefined) {
return await this.messagesService.findAll(findAllMessages, user.userid);
} else client.disconnect(true);
throw new UnauthorizedException({
statusCode: 401,
message: 'Bitte logge dich erst ein',
});
}
authenticated.guard.ts
#Injectable()
export class AuthenticatedGuard implements CanActivate {
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
return request.isAuthenticated();
}
}
Seems like the session is not valid for the websocket routes?

Webflux RSocket Server using RSocketRequestor sending message to Rsocket-js client .Responder not receiving

My requirement is that as and when some event happens on server, it should push updates to client. Want to use RSocket and not SSE.
Will responder configured on the rsocket-js client respond to server request?
If yes then how should the rsocket-js responder be configured to accept message on a particular route? That part please clarify.
Not sure if my spring service is correct.
My Webflux Spring Boot RSocket server code-
#Service
#RequiredArgsConstructor
public class RsocketService {
private final RSocketRequester rSocketRequester;
public void serverToClientRequest(){
Mono.just("Your request is completed at "+ LocalDateTime.now())
.delayElement(Duration.ofSeconds(ThreadLocalRandom.current().nextInt(5, 10)))
.flatMap(m -> rSocketRequester.route("request.stream").data(m).send())
.subscribe();
}
}
I have a Controller -
#MessageMapping("request.stream")
public Flux<String> requestStream(#Payload String requestData) {
}
Client side I am using RSocketWebSocketClient from 'rsocket-websocket-client';
const client = new RSocketClient({
responder: new EchoResponder(),
transport: new RSocketWebSocketClient(
{
url: 'ws://localhost:7000/rsocket',
wsCreator: (url: string) => new WebSocket(url),
debug: true,
}
),
setup: {
dataMimeType: "text/plain",
metadataMimeType: 'message/x.rsocket.routing.v0',
keepAlive: 600000,
lifetime: 180000,
}
});
My reactjs component-
async componentDidMount() {
const rsocket= await client.connect();
console.log('rsocket client connected');
rsocket
.requestStream({
data: "client message",
metadata: String.fromCharCode('request.stream'.length) + 'request.stream'
})
.subscribe({
onComplete: () => {
console.log("request stream completed");
},
onNext: value => {
console.log("on next-->got data from sever");
console.log(value.data);
},
onError: (error: any) => {
console.log("got error with requestResponse");
console.error(error);
},
onSubscribe: sub => {
console.log("subscribe request Stream!");
sub.request(2147483647);
}
});
}
EchoResponder is taken from https://github.com/rsocket/rsocket-js/blob/master/packages/rsocket-examples/src/LeaseClientExample.js. But the responder does not get any message. Any help is appreciated please.

How to push notifications from server to client using Spring Boot RSocket (Backend) and Angular (rsocket-js)?

I am planning to use RSocket for my notifications system. I wanted to use Spring Boot RSocket for my backend (Java) while for my frontend, I will be using Angular using rsocket-js.
I was able to quickly spin-up a request-stream interaction model wherein I can pull-in all the notifications within my system. See code snippet for my backend:
#MessageMapping("streams")
public Flux<Notification> requestStream() {
log.info("Streaming to notifications...");
return streamEventService.retrieveAllNotifications().log();
}
Now on my frontend, I have the following code snippet:
export class RsocketClientService {
// backend ws endpoint
private readonly wsURL = 'ws://localhost:7000/notification';
client: any;
socket: any
constructor() {
this.client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 10000,
lifetime: 180000,
dataMimeType: 'application/json',
metadataMimeType: 'message/x.rsocket.routing.v0',
payload: {
data: 23
}
},
transport: new RSocketWebSocketClient({
url: this.wsURL
}),
responder: new EchoResponder()
});
}
public connect() {
console.log("initializeSocket...")
this.client.connect().subscribe({
onComplete: (socket: any) => {
this.socket = socket;
this.socket.connectionStatus().subscribe( (status: any) => {
console.log("Connection status? ", status);
});
},
onError: (error: any) => {
console.error("Connection onError? " + error);
},
onSubscribe: (cancel: any) => {
console.log("Connection onSubscribe? cancel?");
}
});
}
public retrieveNotifications() {
this.socket.requestStream({
data: null,
metadata: String.fromCharCode('streams'.length) + 'streams'
})
.subscribe({
onComplete: () => {
console.log("onComplete?");
},
onError: (error: any) => {
console.error("onError? error: " + error);
},
onNext: (payload: any) => {
console.log("onNext? payload: ", payload);
},
onSubscribe: (subscription: any) => {
console.log("onSubscribe?");
subscription.request(1000000);
},
});
}
I have a button in the UI that if clicked will call the method retrieveNotifications which will subscribe to the rsocket message mapping method in my backend requestStream.
Everything is working fine and I could see my responses coming in. Now my question would be, what if on my server there is a new data inserted into the database for example, then how can I send a notification message from my backend server to the frontend saying that "Hey! new data was pushed into the database." I am kind of stuck on how the server will be able to use a somehow fire and forget to the client side.
You want to server-side send request to client-side when connect established.
You can get this connect's RSocketRequester from server then using it create one of four method(FNF, Request-reponse, request-stream, stream-stream) to send request to client.
In client-side, you can receive data in EchoResponder class in one of four method above.
It looks like you need to create a new controller function that returns a void and when you insert an object in the DB you pass that object to the front end from this function and in angular you connect to it as you did up...try to check this link for fire and forget approach ... hope this helps https://www.baeldung.com/spring-boot-rsocket

React and rSocket REQUEST_CHANNEL error with Spring Boot

We have a working demo between React and Spring Boot Data Geode using rSocket for fire & forget, request response and request stream but when we try and use request channel we get error:
org.springframework.messaging.MessageDeliveryException: Destination 'quotes' does not support REQUEST_CHANNEL. Supported interaction(s): [REQUEST_STREAM]
So far on web it looks like this ought to be possible from RSocket Git
It's a simple spring boot app with #Controller endpoints that run over rSocket like this:
#Controller
public class RSocketController {
private static final Logger log = LogManager.getLogger(RSocketController.class);
#Autowired
PriceService priceService;
#MessageMapping(value = "quotes")
public Flux<Quote> getQuotes() {
log.info("In getQuotes");
return priceService.generatePrices();
}
}
The generatePrices returns a Flux of prices which works fine in request stream but we would prefer to use request channel for bi-directional comms.
Client versions
"rsocket-core": "0.0.19"
"rsocket-flowable": "0.0.14"
"rsocket-tcp-client": "0.0.19"
"rsocket-websocket-client": "0.0.19"
Client code
const transport = new RSocketWebSocketClient(transportOptions);
const rSocketClient = new RSocketClient({serializers, setup, transport});
​
rSocketClient.connect().subscribe({
onComplete: socket => {
console.log('Client connected to the RSocket Server');
​
socket.requestChannel(Flowable.just({
data: 'foyss',
metadata: String.fromCharCode(6) + 'quotes'
})).subscribe({
onComplete: function() {
console.log(`Channel received end of server stream`);
},
onError: function(err) {
console.log("err", err);
},
onNext: payload => {
console.log(payload);
},
onSubscribe: function(subscription) {
console.log("got subscription");
subscription.request(0x7fffffff);
},
onError: error => {
console.log(error);
},
onSubscribe: cancel => {
console.log('onSubscribe cancel');
}})
},
onError: error => {
console.log(error);
},
onSubscribe: cancel => {
// console.log(cancel);
console.log('onSubscribe cancel');
}
})
Some JS libraries still don't support the request-channel model. Please check the official documentation for your JS lib, eg, for: https://www.npmjs.com/package/ng-rsocket-rxjs
Missing:
Lease Handling
Server Setup
Resume Support
Request Channel
.....

Resources