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

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

Related

Laravel Policy- Allow Unauthenticated or Authenticate user to perform request using Middleware

I just wanted to perform a request while the user is Authorized and Not Authorized.
If a user logged in then I will check if the Post is enabled or not using $post-> status. If the user is a viewer then he will see the Forbidden error. If the user is Super-Admin then he can perform the action. But if I have an unauthenticated user same role will follow as the Viewer Following the unauthenticated user can not see the Post whose status is disabled(0). But If the $post-> status is 1 or enable then an unauthenticated user can be able to view the Post without seeing an unauthorized error.
the shown Code only works for Authenticate users.
role: 1
BlogPolicy
public function view(?User $user, Blog $post)
{
if ($post->status) {
return true;
}
if ($user === null) {
return false;
}
if ($user->hasAnyRole(["super-admin", "admin"])) {
return true;
}
}
BlogController
public function __construct()
{
$this->middleware(['auth:api'], ['except' => []]);
}
public function show($id)
{
$post = Blog::findOrFail($id);
$this->authorize("view", $post);
return response($post);
}
the shown Code only works for unauthenticated users.
role: 2
BlogController
public function __construct()
{
// $this->authorizeResource(Blog::class);
$this->middleware(['auth:api'], ['except' => ["show"]]);
}
In here if I use the ['except' => ["show"]] It can't check if the user is super-admin or viewer but the unauthenticated user can see enable the post and while they try to see disable the post they got 403 what I expected
Is it possible to use the policy if the user is authenticated or not?
I just wanted to combine them if the user is authenticated then follow the rules 1 if not then follow the rules 2 or instructions or something like that

I can only access my auth()->user() in the method where I auth()->login() and not the other methods in the same Controller

I'm trying to implement my own login/logout with passport in a new Controller.
class AuthController extends AccessTokenController
{
use AuthenticatesUsers;
.
.
My login methods works fine:
public function login(ServerRequestInterface $request)
{
if (!auth()->attempt([
'email' => $request->getParsedBody()['email'],
'password' => $request->getParsedBody()['password']
])) {
return response()->json('failed attempt...');
}
auth()->login(User::where('id', Auth::user()->id)->first());
.
.
// I can access auth()->user() here just fine ..
}
But I can't access the authenticated user in the logout method so I can get his tokens and delete them.
public function logout()
{
//I can't access the authenticated user here
return auth()->user();
//return response()->json('Logged out successfully', 200);
}
What am I doing wrong?
Note: I left out anything in the login method that is related to issuing a token because it's not related to the question ..
Update: my routes/api.php
Route::post('register', 'Auth\RegisterController#register');
Route::post('login', 'Auth\AuthController#login');
Route::post('logout', 'Auth\AuthController#logout');
if you are using api then you should send authorization header else it should work for session based authentication
Then you can access the authenticated user using the request
public function logout(Request $request)
{
return $request->user(); //the user that made the request (the authenticated user)
}
Or:
public function logout(Request $request)
{
return Auth::user(); //the user that made the request (the authenticated user)
}

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

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 .

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