Laravel Policy with Spatie Permission check gives 403 for client credentials API request - laravel

I'm using Laravel Policy and checking for permissions created using Spatie's Laravel-Permissions package.
For an API call with client credentials, the authorizeResource() in the Controller constructor returns 403. If this is removed, it returns the expected results.
NpoPolicy.php
public function view(User $user, Npo $npo)
{
return $user->can('npo.view');
}
NpoController.php
public function __construct()
{
$this->authorizeResource(Npo::class);
}
api.php
Route::middleware('client')->resource('/npo', 'NpoController');
API Request
URL: https://my-app.dev/api/npo/1
Method: GET
When I comment out the authorizeResource method in the controller constructor, I get the result as expected:
{
"npos": {
"id":1,
"name":"Bailey and Sons",
"contact_person_name":"Mr. Davion Mayert",
"created_at":"2019-06-13 17:39:25",
"updated_at":"2019-06-13 17:39:25"
}
}
I'm aware that a Laravel policy requires a User model object and that is why the policy is returning 403 response in my case. Is there a general practice to handle API requests (with client credentials) in these cases?

You have missed the second parameter at authorizeResource function so, at the NpoController.php change the authorizeResource to:
$this->authorizeResource(Npo::class, 'npo');

Related

Do I get a 422 HTTP code even though I am logged in?

Do I get a 422 HTTP code even though I am logged in?
From my blade I send an XHR post request. In the route I use the auth middleware. This works. This means you have to be logged in to send the post.
web.php
Route::post('/posts', [PostController::class, 'store'])->middleware(['web', 'auth'])->name('posts.store');
Now I created my own request class to validate the sent data.
PostStoreRequest authorise method
public function authorize()
{
return false;
}
Since I use my own custom request class I get this error message even though I am logged in:
This action is unauthorized.", exception: "Symfony\\Component\\\HttpKernel\\Exception\\AccessDeniedHttpException
I wonder why this is?
You have to check in the authorize() method if the user is authorised for this action. If you have a role system right you can implement this here. For example, only users with the Writer role are allowed to create a post. If you don't have that and you just allow everyone who is logged in, then change the return to true or return auth()->check().
Example without role system:
public function authorize()
{
return true;
// or
return auth()->check();
}
With role System:
public function authorize()
{
return auth()->user()?->isWriter();
}
Important Note: Thank to #matiaslauriti && #Tpojka for the right advice / good review.

Laravel authorization using Gate performance

In the document it is suggested to define Gate inside boot method of the App\Providers\AuthServiceProvider .
public function boot()
{
$this->registerPolicies();
Gate::define('view-payments', function (SomeModel $someModel) {
return $someModel->isPaymentsAllowed ? true : false;
});
}
As you can see SomeModel model is being called on every request to HTTP web server. Is not it is bad to assign permission on every page load?
Is there any functionality provided by the laravel that i can assign permission in session at the time of login so that permission can be fetched through out the application using session without calling Model at every subsequent request?

how to check if user is authenticated with passport (get user from token using laravel-passport)

I am using Passport to log in users to a Laravel API endpoint, users get authenticated using their social accounts (google, facebook) using laravel-socialite package.
the workflow of logging users in and out works perfectly (generating tokens...Etc). The problem is I have a controller that should return data based on whether there is a user logged in or not.
I do intercept the Bearer token from the HTTP request but I couldn't get the user using the token (I would use DB facade to select the user based on the token but I am actually looking whether there is a more clean way already implemented in Passport)
I also don't want to use auth:api middleware as the controller should work and return data even if no user is logged in.
this is the api route:
Route::get("/articles/{tag?}", "ArticleController#get_tagged");
this is the logic I want the controller to have
public function get_tagged($tag = "", Request $request)
{
if ($request->header("Authorization"))
// return data related to the user
else
// return general data
}
Assuming that you set your api guard to passport, you can simply call if (Auth::guard('api')->check()) to check for an authenticated user:
public function get_tagged($tag = "", Request $request)
{
if (Auth::guard('api')->check()) {
// Here you have access to $request->user() method that
// contains the model of the currently authenticated user.
//
// Note that this method should only work if you call it
// after an Auth::check(), because the user is set in the
// request object by the auth component after a successful
// authentication check/retrival
return response()->json($request->user());
}
// alternative method
if (($user = Auth::user()) !== null) {
// Here you have your authenticated user model
return response()->json($user);
}
// return general data
return response('Unauthenticated user');
}
This would trigger the Laravel authentication checks in the same way as auth:api guard, but won't redirect the user away. In fact, the redirection is done by the Authenticate middleware (stored in vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php) upon the failure of the authentication checking.
Beware that if you don't specify the guard to use, Laravel will use the default guard setting in the config/auth.php file (usually set to web on a fresh Laravel installation).
If you prefer to stick with the Auth facade/class you can as well use Auth::guard('api')->user() instead or the request object.
thanks to #mdexp answer
In my case I can resolve my problem with using
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
}
In my controller.

Laravel passport custom error message and status code when user unauthorized

my project is running on Laravel 5.4 and I use passport to make authentication via api with bearer token. everything works fine, but when unauthorized user tries to reach resource that require authentication, the user gets error message 405 method not allowed
but I want response to be 401 unauthorized .
how can change this, and send only response with message, instead of exception? I did research, but couldn't find anything. I use standard laravel middleware for authorization auth:api. my routes grouped in middleware
Route::group(['middleware' => 'auth:api'], function () {
// these routes should return 401 if user not authenticated
}
Well method not allowed exception happens because you are hitting the wrong endpoint. You are posting to a get or vice verca.
However you can modify your exceptions if you go to App\Exception open up handler.php in render() method there you can adjust exceptions as you want example:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
return response('unauthorized', 401);
}
return parent::render($request, $exception);
}
On handler() method just check if $exception is instance of any exception object, if so you can modify the response as you want. For laravel exceptions follow link

Laravel 5.3 API route not saving session between requests

I am trying to build a static HTML viewer through Laravel's 5.3 API routing logic and JWT. The files are all stored on S3 and need to be protected so I thought the best way to do this was to make a kind of proxy that all the files pass through. That way I can check the token of the user from the API request and load the files accordingly.
The first file loads fine.
http://example.com/api/proxy/file.html?token={token}
The issue arises when the HTML file tries to load files from itself. It works when I strip out the authentication functions so I know it's not an issue with getting the files. It's because the token is not appended to future requests. It sends this instead without the token.
http://example.com/api/proxy/some_image.png
I attempted to add the following code to my token checker logic.
public function __construct(JWTAuth $jwtAuth)
{
$this->middleware(function ($request, $next) use ($jwtAuth) {
if (!$jwtAuth->getToken()) {
if (!Auth::user()) {
return response()->error('The token could not be parsed from the request', 400);
} else {
$this->authUser = Auth::user();
}
} else {
$this->authUser = $jwtAuth->parseToken()->authenticate();
Auth::setUser($this->authUser);
}
return $next($request);
});
}
But for some reason this does not work. When the first .html loads up with the token it tries to authenticate the user using Laravel's Auth middleware but Auth::user() returns null on the image request.

Resources