Private channels data does not get received by clients - laravel

I am not able to get private channels to work using Pusher and Laravel broadcasting. In the routes/channels.php file it seems that none of the function gets fired:
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('testevent.{id}', function ($user, $id)
{
//This never fires
dd("ENTERED HERE");
return TRUE;
});
In the BroadcastServiceProvider.php I have:
public function boot()
{
Broadcast::routes(['middleware' => 'auth:api']);
require base_path('routes/channels.php');
}
The Javascript file handling data on the client side (using Echo):
Echo.private('testevent.1').listen('TestEvent', function(e)
{
console.log(e);
});
Using public channels works perfect. But as soon as I try to create private channels the data is not sent to the client listening for the data. What could the problem be?
Thanks for any help and guidance!
EDIT:
In the Pusher web console it does not appear that the client has subscribed for the "testevent.1" channel. If I change this to a public channel the subscription gets registered.

At the "Defining authorization callbacks" paragraph of the Laravel Broadcast documentation, you can see that privates channels need to authenticate users before that they be able to listen to them.
So in in your routes/channels.php, you need there to write authentication logic, for exemple :
Broadcast::channel('testevent.{id}', function ($user, $id)
{
return $user->id === $user::findOrFail($id);
});

Related

Why I can not get message pusher in Laravel?

Here is a private channel:
public function broadcastOn()
{
// Here I get $user object successfully
return new PrivateChannel('user-'.$this->user->id); // it is 1
}
The permissions are:
Broadcast::channel('user-{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
The sender is:
Route::post('sender', function () {
PrivateEvent::dispatch(Auth::user());
})->middleware('auth:sanctum');
The client was tested in pusher debug console, it works:
var channel = pusher.subscribe('user-1');
channel.bind('PrivateEvent', function(data) {
alert(JSON.stringify(data));
});
Why when I send message using routing I dont get the message in JS? JS has not errors too.

laravel broadcasting/auth 403 (Forbidden) while user is logged in

I am using beyondcode/laravel-websocket in my laravel app.
I am trying to use private channel for logged in user. but while the user is logged in. I face 403 error.
my channel.php
Broadcast::channel('user.{id}', function ($user , $id) {
return $user->id == $id;
});
client side:
Echo.private('user.{{$loggedUser->id}}')
.listen('MessageSent' , e => {
console.log(e);
});
the user utilizes the default guard. I also using multiguard system but for default user this shouldn't be a problem.
my BroadcastServiceProvider.php:
public function boot()
{
Broadcast::routes(['middleware' => ['web','auth','auth:organ']]);
require base_path('routes/channels.php');
}
note: public channel works fine

Getting 403, error when using private channels with laravel broadcast

Unable to authenticate users in my chat app. I am getting a 403 error from the console. This happens though when I use private channels, but when using a public channel, this is working really fine but I definitely want authenticated users only.
It is more like an spa, hence using axios for almost everything including user authentication requests to laravel.
below is my code:
BroadcastServiceProvider:
` public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}`
Channels.php:
`Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('chat', function ($user) {
return Auth::check();
});
`
listen directive from vue component:
`Echo.private('chat')
.listen('.App\\Events\\Chats\\MessageSent', (e) => {
console.log(e);
this.sentMessages.push({
message: e.message.message,
user: e.user
});
`
MessageSent event:
` public function broadcastOn()
{
return new PrivateChannel('chat');
}
`
Now using the vue-echo wrapper but still I got this problem, I still haven't figured out what I am missing
It's is just basically as the error suggests, an authentication problem, well I am using tokens for authentication in my app but now needed to also pass this token issued to the user as well to vue-echo.
And also change:
Broadcast::routes();
to
Broadcast::routes(['middleware' => ['auth:api']]);
since am creating a single page application hence using axios for authentication which therefore interprets to me using the api middleware.
Guided by the answer provided by Alex on the question 'Laravel /broadcasting/auth Always Fails With 403 Error
'
You can get more details there.
Thanks all .

Laravel Notifications - delay email sending and cancel if condition met

I have an app where I am sending a push notification which is fine if the user is logged into the application - however, if they're not / if they have not read the notification within X minutes I'd like to send them an email.
The way I am going about this is to use Laravel Notifications to create a mail, broadcast & database notification. On the toMail() method I'm returning a mailable with a delay -
public function toMail($notifiable)
{
return (new \App\Mail\Order\NewOrder($this->order))
->delay(now()->addMinutes(10));
}
After the minutes are up, the email will send but, before the send goes ahead I'd like to perform a check to see if the push/database notification has already been marked as read and if it has cancel the email send. The only way I can think to do this is to bind to the MessageSending event that is baked into Laravel -
// listen for emails being sent
'Illuminate\Mail\Events\MessageSending' => [
'App\Listeners\Notification\SendingEmail'
],
The only problem is this listener receives a Swift mail event and not the original mailable I was dispatching so I don't know how to cancel it. Any ideas and thanks in advance?
Class extends Notification
public function via($notifiable)
{
if($this->dontSend($notifiable)) {
return [];
}
return ['mail'];
}
public function dontSend($notifiable)
{
return $this->appointment->status === 'cancelled';
}
Class EventServiceProvider
protected $listen = [
NotificationSending::class => [
NotificationSendingListener::class,
],
];
Class NotificationSendingListener
public function handle(NotificationSending $event)
{
if (method_exists($event->notification, 'dontSend')) {
return !$event->notification->dontSend($event->notifiable);
}
return true;
}
For more details look article Handling delayed notifications in Laravel

How to logout a user from API using laravel Passport

I'm currently using 2 projects. 1 front end (with laravel backend to communicate with API) and another laravel project (the API).
Now I use Laravel Passport to authenticate users and to make sure every API call is an authorized call.
Now when I want to log out my user, I send a post request to my API (with Bearer token) and try to log him out of the API (and clear session, cookies,...)
Then on the client I also refresh my session so the token is no longer known. Now when I go back to the login page, it automatically logs in my user. (Or my user is just still logged in).
Can someone explain me how to properly log out a user with Laravel passport?
Make sure that in User model, you have this imported
use Laravel\Passport\HasApiTokens;
and you're using the trait HasApiTokens in the User model class using
use HasApiTokens
inside the user class.
Now you create the log out route and in the controller,
do this
$user = Auth::user()->token();
$user->revoke();
return 'logged out'; // modify as per your need
This will log the user out from the current device where he requested to log out. If you want to log out from all the devices where he's logged in. Then do this instead
$tokens = $user->tokens->pluck('id');
Token::whereIn('id', $tokens)
->update(['revoked'=> true]);
RefreshToken::whereIn('access_token_id', $tokens)->update(['revoked' => true]);
Make sure to import these two at the top
use Laravel\Passport\RefreshToken;
use Laravel\Passport\Token;
This will revoke all the access and refresh tokens issued to that user. This will log the user out from everywhere. This really comes into help when the user changes his password using reset password or forget password option and you have to log the user out from everywhere.
You need to delete the token from the database table oauth_access_tokens
you can do that by creating a new model like OauthAccessToken
Run the command php artisan make:model OauthAccessToken to create the model.
Then create a relation between the User model and the new created OauthAccessToken Model , in User.php add :
public function AauthAcessToken(){
return $this->hasMany('\App\OauthAccessToken');
}
in UserController.php , create a new function for logout:
public function logoutApi()
{
if (Auth::check()) {
Auth::user()->AauthAcessToken()->delete();
}
}
In api.php router , create new route :
Route::post('logout','UserController#logoutApi');
Now you can logout by calling posting to URL /api/logout
This is sample code i'm used for log out
public function logout(Request $request)
{
$request->user()->token()->revoke();
return response()->json([
'message' => 'Successfully logged out'
]);
}
Create a route for logout:
$router->group(['middleware' => 'auth:api'], function () use ($router) {
Route::get('me/logout', 'UserController#logout');
});
Create a logout function in userController ( or as mentioned in your route)
public function logout() {
$accessToken = Auth::user()->token();
DB::table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true
]);
$accessToken->revoke();
return response()->json(null, 204);
}
I am using Laravel 6.12.0, below function is working for me.
public function logout(Request $request){
$accessToken = Auth::user()->token();
$token= $request->user()->tokens->find($accessToken);
$token->revoke();
$response=array();
$response['status']=1;
$response['statuscode']=200;
$response['msg']="Successfully logout";
return response()->json($response)->header('Content-Type', 'application/json');
}
This is my first post.. and i find a clean solution (Laravel last Version)
/**
* Logout api
*
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
if (Auth::check()) {
$token = Auth::user()->token();
$token->revoke();
return $this->sendResponse(null, 'User is logout');
}
else{
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised'] , Response::HTTP_UNAUTHORIZED);
}
}
Below is the simplest way I found to do it.
1. USE database SESSION INSTEAD OF file SESSION
Official documention
php artisan session:table
php artisan migrate
Replace SESSION_DRIVER=file by SESSION_DRIVER=database in your .env file.
2. DELETE USER SESSION RIGHT AFTER LOGIN
After a user is redirected to your frontend and logs in to finally get a token, you probably call a route in api/routes.php to get the user information, that's where I'm closing the user backend session before sending back user information to the frontend:
Route::middleware('auth:api')->get('/user', function (Request $request) {
// Close user session here
Illuminate\Support\Facades\DB::table('sessions')
->whereUserId($request->user()->id)
->delete();
return $request->user();
});
3. REVOKE TOKENS AT LOGOUT
Then, to "log out" (actually, revoke tokens) the user from the frontend, you just need to call another route to revoke the token and refresh_token:
Route::middleware('auth:api')->post('/logout', function (Request $request) {
// Revoke access token
// => Set oauth_access_tokens.revoked to TRUE (t)
$request->user()->token()->revoke();
// Revoke all of the token's refresh tokens
// => Set oauth_refresh_tokens.revoked to TRUE (t)
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($request->user()->token()->id);
return;
});
You may prefer to put these two closures in the UserController.
Hope help someone:
if (Auth::check()) {
$request->user()->tokens->each(function ($token, $key) {
$token->delete();
});
}
Good Luck.
I use this in my project to logout from multiple device.
public function logout(Request $request, $devices = FALSE)
{
$this->logoutMultiple(\Auth::user(), $devices);
return response()->json([], 204);
}
private function logoutMultiple(\App\Models\User $user, $devices = FALSE)
{
$accessTokens = $user->tokens();
if ($devices == 'all') {
} else if ($devices == 'other') {
$accessTokens->where('id', '!=', $user->token()->id);
} else {
$accessTokens->where('id', '=', $user->token()->id);
}
$accessTokens = $accessTokens->get();
foreach ($accessTokens as $accessToken) {
$refreshToken = \DB::table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update(['revoked' => TRUE]);
$accessToken->revoke();
}
}
Try this code to help you to logout from passport authentication.
Route::post('/logout', function(){
if (Auth::check()) {
Auth::user()->AauthAcessToken()->delete();
}
return response()->json([
'status' => 1,
'message' => 'User Logout',
], 200);
});
check whether your model contains OauthAccessToken which needs to connect with the database oauth_access_tokens. The access token is stored in the database table oauth_access_tokens. and makes a relation from users to oauth_access_tokens.
public function AauthAcessToken(){
return $this->hasMany(OauthAccessToken::class);
}
You can use following code to remove to token for logged in user.
$request->user()->token()->revoke();
If you want to learn about this in-depth then watch this tutorial:
https://www.youtube.com/watch?v=UKSQdg1uPbQ
public function logout(Request $request)
{
$request->user()->token()->revoke();
if ($request->everywhere) {
foreach ($request->user()->tokens()->whereRevoked(0)->get() as $token) {
$token->revoke();
}
}
return response()->json(['message' => 'success']);
}

Resources