1

Unable to find answers anywhere

I am using Laravel 5.5 policy to restrict a user from listing properties that are not registered by the user (authenticated via API). I have two classes User and HostProperty. Additionally, I have registered a policy for the user to access their hosted property list by ID.

Here are my models.

The Main Problem is not able to call on controller method - which throws above error:

$authUser = auth('api')->user();
if ($authUser->can('access', $property)) {
              return response()->json(['success' => 'success']); 
            } else {
              return response()->json(['error' => 'error']); 
            }

User.php

namespace App;

use Illuminate\Notifications\Notifiable;
use Cartalyst\Sentinel\Users\EloquentUser;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Passport\HasApiTokens;
use Illuminate\Auth\Authenticatable as AuthenticableTrait;
use Illuminate\Contracts\Auth\Authenticatable;

class User extends EloquentUser implements Authenticatable
{
    use HasApiTokens, Notifiable;
    use SoftDeletes;
    use AuthenticableTrait;
    protected $guarded=[];
    protected $dates = ['deleted_at'];
    protected $hidden = [
        'password', 'remember_token',
    ];

    //hosts relation
    public function hostproperty()
    {
        return $this->hasMany('App\Models\Hosts\HostProperty','user_id');
    }
}

HostProperty.php

namespace App\Models\Hosts;

use Illuminate\Database\Eloquent\Model;

class HostProperty extends Model
{
    public $timestamps = true;
    protected $guarded=[];
    protected $hidden = [
        'user_id',
    ];
    public function user()
    {
        return $this->belongsTo('App\User','user_id');
    }
}

HostPropertyPolicy

namespace App\Policies\Host;

use App\User;
use App\Models\Hosts\HostProperty; 
use Illuminate\Auth\Access\HandlesAuthorization;

class HostPropertyPolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    public function access(User $user, HostProperty $HostProperty)
    {return TRUE;
        //return $user->id === $HostProperty->user_id;
    }
}

AuthServiceProvider

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Models\Hosts\HostProperty;
use App\Policies\Host\HostPropertyPolicy;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
        HostProperty::class=>HostPropertyPolicy::class,
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
         Passport::routes();

    }
}

HostPropertyController

    use App\User;
    use App\Models\Hosts\HostProperty; 
    use App\Http\Controllers\Controller;
class HostPropertyController extends Controller
{
    public function listOneProperty($propertyId)
        {
            $authUser = auth('api')->user();
            $property=HostProperty::with('user')->find($propertyId);
            if ($authUser->can('access', $property)) {
              return response()->json(['success' => 'success']); 
            } else {
              return response()->json(['error' => 'error']); 
            }
    }

}

Route

Route::get('listOneProperty/{propertyId}',  array('as' => 'listOneProperty.get', 'uses' => 'HostPropertyController@listOneProperty'));

Please note: I am calling from API - the above route is for API, I am not able to use the policy on the API routes. I keep getting the above error while calling this route.

I tried

$this->authorize('access', $property);

However, since API doesn't store login session the above could not be completed so I again tried with

$authUser = auth('api')->user();
$authUser->authorize('access', $property);

Does not work either. I have tried all I can but still, I cannot get it done right.

If someone has an example of using Laravel policy in API authenticated by Passport it would be helpful for anybody looking to get this done right.

With regards

Deepesh Thapa
  • 1,721
  • 3
  • 19
  • 29

2 Answers2

1

Looks like

$authUser = auth('api')->user();

is returning a query instead of a User model.

Please make sure that $authUser is a User model before calling ->can()

  • I tried with $user=User::find(1); Still returns the same error – Deepesh Thapa Nov 16 '18 at 09:54
  • Have you tried to use ```$this->authorize('access', $property);``` More info at https://laravel.com/docs/5.7/authorization#via-controller-helpers – Adrian Hernandez-Lopez Nov 16 '18 at 10:04
  • I am using API, the authorize method is only useful for web routes I believe. I used that too, I have mentioned it above, it keeps returning unauthorize when I use that – Deepesh Thapa Nov 16 '18 at 10:06
  • Since API authentication is done using passport tokens, I dont think it will take the current authenticated user. – Deepesh Thapa Nov 16 '18 at 10:06
  • 2
    Here is a related post that may help https://stackoverflow.com/questions/41799974/authorization-policies-gates-for-laravel-5-3-web-app-consuming-own-api-w-passpo – Adrian Hernandez-Lopez Nov 16 '18 at 10:09
  • Yes Thank you .. The above stackoverflow answer worked for me. I had to create a new middleware and use Auth::shouldUse('api'); on every API calls for user to be available. Thank you – Deepesh Thapa Nov 16 '18 at 11:34
1

However, since API doesn't store login session the above could not be completed so I again tried with

Authentication is typically handled by middleware in Laravel. If you're using Passport, you should be able to use the auth:api middleware to authenticate requests, as long as you're sending the correct Authorization header and token.

Try changing this

$authUser = auth('api')->user();

to this

$authUser = auth()->user();

And add the auth middleware to your route:

Route::get('listOneProperty/{propertyId}', 'HostPropertyController@listOneProperty')
     ->name('listOneProperty.get')
     ->middleware('auth:api');

If you're consuming the api from your own web views and you want it to work with the current session, check out the Consuming Your API With JavaScript section of the Passport docs: https://laravel.com/docs/5.7/passport#consuming-your-api-with-javascript

Travis Britz
  • 5,094
  • 2
  • 20
  • 35