I got laravel-echo-server and Laravel 5 application with vuejs, and I'm trying to connect front end to back end via sockets.
I've managed to connect everything together via Echo.channel() method, however it will not subscribe with Echo.private()
This is what I got so far :
Once the page loads I call :
I initilise the Echo via
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
csrfToken : Laravel.csrfToken,
auth : {
headers : {
Authorization : "Bearer b6f96a6e99e90dzzzzzzz"
}
}
});
Then I create a new event via vue resourse
Vue.http.get('/api/errors/get');
This fires laravel event(new \App\Events\ErrorsEvent()); event
Which Broadcasts the event privately via
public function broadcastOn()
{
return new PrivateChannel('errors');
}
At this point laravel-echo-server responds with
Channel: private-errors
Event: App\Events\ErrorsEvent
CHANNEL private-errors
Then i try to subscribe to the channel with echo by running
Echo.private('errors')
.listen('ErrorsEvent', (e) => {
this.errors = e.errors;
console.log(e);
});
At which laravel-echo-server responds with
[14:47:31] - xxxxxxxxxxxxxx could not be authenticated to private-errors
Client can not be authenticated, got HTTP status 403
Does anybody know if I'm missing something here?
EDIT
This is my BroadcastServiceProvider
public function boot(Request $request)
{
Broadcast::routes();
/*
* Authenticate the user's personal channel...
*/
Broadcast::channel('App.User.*', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
}
Also, I've discovered that if I try to subscribe to
Echo.private('App.User.2')
.listen('ErrorsEvent', (e) => {
this.errors = e.errors;
console.log(e);
});
It connects and everything is ok, however it still refuses to connect with the errors channel
My issue was that I hadn't noticed there are two BroadcastServiceProvider::class entries in app/config.php
I had only checked the first one. Once I uncommented App\Providers\BroadcastServiceProvider::class I didn't need to specify the bearer token.
I believe the reason you couldn't connect on the errors channel in your above config is that you need to change your call in your BroadcastServiceProvider (or routes/channels.php) to
Broadcast::channel('errors', function ($user) {
return true; //replace with suitable auth check here
});
Related
I'm trying to implement server sent events in a Laravel project.
I created an endpoint in the backend that looks like this:
public function stream(Request $request)
{
$response = new StreamedResponse(function () use ($request) {
$i = 0;
while (true) {
$i++;
echo 'data: ' . $i . "\n\n";
ob_flush();
flush();
if (connection_aborted()) {
break;
}
usleep(20000);
}
});
$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('X-Accel-Buffering', 'no');
$response->headers->set('Cache-Control', 'no-cache');
return $response;
}
The frontend part:
<script>
console.log("script started");
const eventSource = new EventSource('/stream');
eventSource.onmessage = function(event) {
console.log(event);
}
</script>
When I run the project with the builtin Laravel webserver the event stream seems to be working correctly and it looks like this in the console of the browser:
The problem is that all the other requests to my backend are blocked. So I tried using nginx and apache webserver. But unfortunately I'm running into a different problem then. The connection does not establish (readyState 0) and approximately every 5 seconds a new request to the /stream endpoint is generated:
I tried updating the nginx configuration as described here:
Nginx configuration
and I used other implementations of sse:
sse in laravel
I'm running out of ideas why this is not working. For this project websockets and polling are not an alternative. Hopefully someone of you has an idea and can help me with this.
I am building a real-time chat application with Laravel and Nuxt (Front-end and Back-end separated) using Pusher and Laravel-Echo, I have configured Laravel with pusher and it works fine and can see my requests in the Pusher debug console, I also want to mention two things first, I handle my authentication using Laravel-JWT and Nuxt-Auth and second, it works with public channels but since I converted it to private, Nuxt client can not subscribe anymore.
Here is error image:
Here is my Laravel config:
.env
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=MY_APP_ID
PUSHER_APP_KEY=MY_APP_KEY
PUSHER_APP_SECRET=MY_APP_SECRET
PUSHER_APP_CLUSTER=eu
channels.php
Broadcast::channel('chat', function() {
return true;
});
ChatController.php
public function sendMessage(Request $req) {
$user = Auth::user();
broadcast(new ChatEvent($user, $req->message))
->toOthers();
}
ChatEvent.php
public function __construct(User $user, $message)
{
$this->user = $user;
$this->message = $message;
}
public function broadcastAs()
{
return 'chat-event';
}
public function broadcastOn()
{
return new PrivateChannel('chat');
}
EventServiceProvider
use App\Events\ChatEvent;
use App\Listeners\SendMessageNotification;
...
protected $listen = [
ChatEvent::class => [
SendMessageNotification::class,
],
];
composer.json
"require": {
...
"pusher/pusher-php-server": "^7.0",
"tymon/jwt-auth": "^1.0"
}
Here is my Nuxt config:
package.json
"dependencies": {
...
"#nuxtjs/auth-next": "5.0.0-1624817847.21691f1",
"nuxt": "^2.14.6",
"pusher-js": "^7.0.3",
},
"devDependencies": {
...
"#nuxtjs/laravel-echo": "^1.1.0",
}
nuxt.config.js
buildModules: [
...
'#nuxtjs/laravel-echo',
],
echo: {
broadcaster: 'pusher',
key: 'my-app-key',
cluster: 'eu',
forceTLS: true,
plugins: ['~/plugins/echo.js']
},
echo.js plugin
export default function ({ $echo }) {
console.log($echo)
}
chat page index.vue
mounted() {
this.$echo.private(`chat`)
.listen(`chat-event`, (e) => {
this.addNewMessage(e.user, e.message)
})
.listenForWhisper('typing', (e) => {
console.log(e.name);
})
},
watch: {
typeMessage() {
this.$echo.private(`chat`)
.whisper('typing', {
name: this.typeMessage
})
}
},
Here is my echo console log:
My attempts to fix the error
I tried to test the back-end with the postman, and the pusher debug console works fine:
I tried to change the channel to public, and it works fine.
Conclusion
I think the problem comes from the authentication part when trying to subscribe to a private channel, hope anyone can help!
You are clearly facing a connection issue between your client and your API because of authentication.
Try with basic config without forceTLS (it must be also disabled in pusher website under App settings section).
In addition, be sure that your echo instance is hitting your auth endpoint with the required headers. It's needed for private/presence channels.
In my case I'm using passport so auth must be done with Bearer token attached in the header.
broadcaster: 'pusher',
key: process.env.VUE_APP_PUSHER_KEY,
cluster: process.env.VUE_APP_PUSHER_CLUSTER,
authEndpoint: process.env.VUE_APP_API_URL + '/broadcasting/auth',
auth: {
headers: {
Authorization: 'Bearer ' + this.$auth.token()
}
}
EDIT: sorry for not clarifying enough.
Presence/private channels are allowed only for authenticated users. Since you are using JWT you need to provide an Authorization header with the Bearer access token that you get when the user logs in.
I'm not sure how nuxt handles user tokens, but the this.$auth.token() is just a reference to how I retrieve a user's token by using websanova vue auth. But you may handle and store the token at the user's end in many ways
currently i am working on a mobile application using react native, and as back end i am using Laravel.
As a channels client library at the client side i am using Pusher
This is how my channel route looks like in the back end
Broadcast::channel('conversation.{message_id}', function ($user,$id) {
return true;
});
I can broadcast successfully, but at pusher debug console i've noticed that a prefix (private-) added to the route.
Here is how i listen to event
const pusher = new Pusher('XXXX', {
cluster: 'eu',
authEndpoint: 'http:backend.test/api/broadcasting/auth',
auth: {
headers: {
Authorization: `Bearer `+token,
Accept: 'application/json',
},
},
});
const channel = pusher.subscribe('private-conversation.7');
channel.bind('App\\Events\\MessageSent', function (data) {
console.log("event")
console.log(data);
});
As you can see in my code i subscribe to "private-conversation.7", pusher debug console shows that the client is subscribed to channel successfully, but the issue is that the client didn't listen to events, when i create a custom event in pusher and set "conversation.7" as a channel name (i removed private- prefix) the event triggered with success.
Any suggestions please?
That is a convention used to separate private, public and presence channel.
When you subscribe a channel with private- prefix, it will require the auth process.
Laravel will automatically add private- prefix to channel name when broadcast.
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\PrivateChannel
*/
public function broadcastOn()
{
return new PrivateChannel('orders.'.$this->order->id);
}
Here are reference docs:
https://pusher.com/docs/channels/using_channels/private-channels
I am adding CcAvenue gateway in laravel 5.3 on PHP 7.2, everything working fine till the payment page of CcAvenue, but after payment is done or payment canceled by the user, the return response URL is showing the following error
"Oops! An Error Occurred
The server returned a "405 Method Not Allowed".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused."
My return URL is this: https:// www.domainname.com/booking/cancel/cc_checkout_gateway?c=f4b7d25d6e894a44725fff59adafcf82
Code in the Routes file
use Illuminate\Support\Facades\Route;
// Booking
Route::group(['prefix'=>config('booking.booking_route_prefix')],function(){
Route::post('/addToCart','BookingController#addToCart');
Route::post('/doCheckout','BookingController#doCheckout')->name('booking.doCheckout');
Route::get('/confirm/{gateway}','BookingController#confirmPayment');
Route::get('/cancel/{gateway}','BookingController#cancelPayment');
Route::get('/{code}','BookingController#detail');
Route::get('/{code}/checkout','BookingController#checkout');
Route::get('/{code}/check-status','BookingController#checkStatusCheckout');
//ical
Route::get('/export-ical/{type}/{id}','BookingController#exportIcal')->name('booking.admin.export-ical');
//inquiry
Route::post('/addEnquiry','BookingController#addEnquiry');
});
Route::group(['prefix'=>'gateway'],function(){
Route::get('/confirm/{gateway}','NormalCheckoutController#confirmPayment')->name('gateway.confirm');
Route::get('/cancel/{gateway}','NormalCheckoutController#cancelPayment')->name('gateway.cancel');
Route::get('/info','NormalCheckoutController#showInfo')->name('gateway.info');
});
Code in BookingController.php
public function cancelPayment(Request $request, $gateway)
{
$gateways = get_payment_gateways();
if (empty($gateways[$gateway]) or !class_exists($gateways[$gateway])) {
return $this->sendError(__("Payment gateway not found"));
}
$gatewayObj = new $gateways[$gateway]($gateway);
if (!$gatewayObj->isAvailable()) {
return $this->sendError(__("Payment gateway is not available"));
}
return $gatewayObj->cancelPayment($request);
}
Code in Gateway CcCheckoutGateway.php
public function cancelPayment(Request $request)
{
$c = $request->query('c');
$booking = Booking::where('code', $c)->first();
if (!empty($booking) and in_array($booking->status, [$booking::UNPAID])) {
$payment = $booking->payment;
if ($payment) {
$payment->status = 'cancel';
$payment->logs = \GuzzleHttp\json_encode([
'customer_cancel' => 1
]);
$payment->save();
// Refund without check status
$booking->tryRefundToWallet(false);
}
return redirect($booking->getDetailUrl())->with("error", __("You cancelled the payment"));
}
if (!empty($booking)) {
return redirect($booking->getDetailUrl());
} else {
return redirect(url('/'));
}
}
After too much R&D I found that my routes code is allowing method is GET & HEAD, but Ccavenue response URL is sending the response in POST method
I have tried every possible solution changed
Route::get('/cancel/{gateway}','BookingController#cancelPayment');
to
Route::post('/cancel/{gateway}','BookingController#cancelPayment');
and
Route::any('/cancel/{gateway}','BookingController#cancelPayment');
but after that it showing error 419: page expired
Please tell me how can I resolve the above issue.
The Sanctum Auth system on my local machine works well and I have no errors. But my deployed app is having trouble with authorizing a user. When I login it sends a request to get the user data then redirects. After auth completes you are redirected and the app make a GET request for more data. This GET route is guarded using laravel sanctum. But the backend is not registering that the user has made a successful login attempt so it sends a 401 Unauthorized error. Here is some code...
loadUser action from store.js
actions: {
async loadUser({ commit, dispatch }) {
if (isLoggedIn()) {
try {
const user = (await Axios.get('/user')).data;
commit('setUser', user);
commit('setLoggedIn', true);
} catch (error) {
dispatch('logout');
}
}
},
}
Route Guard on the routs.js checking to see isLoggedIn (which is just a boolean store locally)
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// if (to.meta.requiresAuth) {
if (isLoggedIn()) {
next();
} else {
next({
name: 'home'
});
}
} else {
next();
}
})
It was pointed out that I had forgotten the withCredetials setting for axios in bootstrap.js. I made this addition but my issue still remains.
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.withCredentials = true;
Route middleware guard on the server side (this is where the request is getting turned away)
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('trucks', 'Api\TruckController');
});
In the laravel cors.php config file I changed the "supports_credentials" from false to true
'supports_credentials' => true,
It seems to me that the cookie information is not being over the api call (but I'm really not sure). This setup is working on my local machine but not on the server that I have deployed to.
Needed to add an environment variable to the .env file for SANCTUM_STATEFUL_DOMAINS and made that equal the domain name.
In the laravel sanctum.php config file...
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),