Getting 403, error when using private channels with laravel broadcast - laravel

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 .

Related

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

How to user Laravel resource with /user api of Authentication

I am building a project with Laravel and Vue using Axios and Passport.
My authentication is working and generating token and saving in my local storage to check for login.
I am also getting the data using
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
However, my user also has some relationship which I have described in my model like
public function types()
{
return $this->belongsTo(types::class, 'type_id');
}
and my user resource looks like this
public function toArray($request)
{
$array = parent::toArray($request);
$array['group'] = $this->groups;
$array['type'] = $this->types->typeName;
return $array;
}
So when user login I am trying to get user data using auth:api however I want a relationship to come with it.
I tried
Route::middleware('auth:api')->get('/user', function (Request $request) {
return UserResource::collection($request->user());
});
and get error
> Call to undefined method App\User::mapInto()
I also tried
```php
return new UserResource::collection($request->user());
error: syntax error, unexpected 'collection' (T_STRING), expecting variable (T_VARIABLE) or '$'
return UserResource::collection($request->user()-get());
error: Trying to get property 'typeName' of non-object
So what am I doing wrong? thanks for your time and if need more details please let me know
You can achieve similar, if not better results using Eager loading. source
Sample:
Route::get('me', function(Request $request) {
$request->user()->load('types');
});

Problem getting authenticated user with Laravel Lumen + Passport

I am using LumenPassport (https://github.com/dusterio/lumen-passport) and I followed a few tutorials listed here.
I used a combination of these tutorials as well as a heck of google and stackoverflow searches to achieve what I have thus far:
http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/
http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/
https://blog.pusher.com/make-an-oauth2-server-using-laravel-passport/
What I achieved so far
1. Using password grant to get an access & refresh token
2. Storing these tokens in a secure http only cookie
3. Retrieving these tokens in Lumen's AuthServiceProvider
What I am unable to do
1. Getting the authenticated user with the AccessToken
I am trying to access either of these endpoints:
$router->group(['middleware' => 'auth:api'], function () use ($router) {
$router->get('/', function () use ($router) {return $router->app->version();});
$router->post('/logout', '\App\Auth\LoginController#logout');
});
I will immediately get an unauthorized error.. After some deep diving, the error comes from Authenticate.php which I know is called after AuthServiceProvider. I took a look at AuthServiceProvider and according to Lumen's documentation, this is how the boot method should looks like. Of course it is using the "api" driver and I had to switch it to "passport" for it to work.
AuthServiceProvider.php
public function boot()
{
$this->app['auth']->viaRequest('passport', function ($request) {
// dd("test") // this works
// dd(Auth::user());
// dd($request->user());
// dd(Auth::guard('api')->user());
});
}
Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
$status = Response::HTTP_UNAUTHORIZED;
return response()->json(['success' => false, 'status' => $status, 'message' => 'HTTP_UNAUTHORIZED'], $status);
}
return $next($request);
}
From here, I am still unable to get any of the authenticated user's information. I have made sure to access these endpoints with Postman with the appropriate Authorization headers.
The reason why I need to retrieve the user is because I hope that in my logout method, I will be able to then retrieve the accessToken of that authenticated user and revoke the token and clear the cookies.
LoginController.php
public function logout()
{
// Get the accessToken from Auth
// Need to fix AuthServiceProvider first
$accessToken = $this->auth->user()->token();
$refreshToken = $this->db
->table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true,
]);
$accessToken->revoke();
$this->cookie->queue($this->cookie->forget(self::REFRESH_TOKEN));
}
At that point you cannot use Auth::user() since that function is the functionality for resolving that. So what you need to do is extract the bearer token with $request->bearerToken() and use that to retrieve your user.
Update
I took a look at your code and I would recommend the following:
An API is recommended to be 'stateless' meaning that it should not persist any state (i.e. cookies). It is far better to pass the access token with each request and let the application that accesses your API handle the tokens. Therefore I would recommend to remove the log-out functionality. Then you can do the following in your AuthServiceProvider:
if ($token_exists) {
$user = User::find($token->user_id);
return $user;
}

Private channels data does not get received by clients

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);
});

Laravel Passport post route test on postman

I set up Laravel Passport and started to create a few Get routes to get some data, that worked fine.
Now I am trying to post to get on auth token, which currently does not work:
This is my route which I call (Get route works, Post route does not work):
Route::group(['middleware' => 'auth:api'], function ()
{;
Route::get('users', ['as' => 'users', 'uses' => 'ApiController#users']);
Route::post('login/{id}/{name}', ['as' => 'login', 'uses' => 'ApiController#login']);
});
The method in my ApiController looks currently like this:
public function login(Request $request, $id, $name)
{
if($request->isMethod('post'))
{
$id = $request->id;
$name = $request->name;
$inquiry = new Inquiry();
$inquiry->user_id = $id;
$inquiry->user_name = $name;
if($inquiry->save())
{
return redirect()->route('inquiry.index')->with('success', 'Success.');
}
else
{
return redirect()->route('inquiry.index')->with('error', 'An error accured.')->withInput();
}
}
else
{
dd("Use Post.");
}
}
I tried to call it with following options:
Edit
I somehow managed to get this work after many hours, but still dont understand something.
First I did following:
public function callback(Request $request)
{
dd($request->code) // this holds a token I need for the code parameter in the post
...
With that I could get the token for the code parameter, but I think there is a better way to do that.
And finally this is now how I get the access + refresh token:
But there has to be a better way to get the code token of the callback request ($request->code), instead of dumping it and copying it.
The problem is that you have your login route inside a route group with auth:api on it. This means the user needs to be authenticated to even be able to authenticate. Just remove the login route outside the group and you should be fine.
You should call the Passport::routes method within the boot method of your AuthServiceProvider. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens:
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
/oauth/authorize route is already defined by the Passport::routes method. You do not need to manually define this route.

Resources