Search code examples
angularlaravelauthenticationjwtmiddleware

Laravel issue with security in profile page


I have been having problems with my site in Laravel for some time. I have an Angular frontend and I'm using an api to query the specific user's data so that a specific user can change his data in his profile page. With that said, I obviously don't want unauthorized people to be able to go to other profile pages to change their data as well.

For example: On my page, with the URL .../user/1 the user can query his data. The problem with this is: If you change the 1 in .../user/1 to .../user/2 for example, the person can access to the data of the person with id 2.

I tried following: In my api.php:

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

// Get Specific User
Route::get('user/{id}' ,'App\Http\Controllers\UserController@getUserById')->middleware('auth');

Unauthorized users can no longer access the profile page, but all Authorized users can access all other users and change their data.

I also tried: $id = Auth::id(), but this returns me Attempt to read property "id" on null

The problem seems to me to be quite complicated, as I somehow need the id of the users currently logged in and make sure they can't access other users id. Do you have any idea how I could best do this?

I am using Laravel 8

Thanks a lot!

EDIT

Thank you for the detailed answer, you gave me hope. I did everything as you described.

First I created with php artisan make:middleware ResourceOwner middleware and initialized it in the Kernel.php under protected $routeMiddleware like that:'ResourceOwner' => \App\Http\Middleware\ResourceOwner::class, and put in the function like you described.

But unfortunately, I get now 401 (Unauthorized) no matters what ID I pass and with which user I am logged in. I tried:

1. auth('api')->user(); 
2. $request->user('api'); 
3. Auth::guard('api')->user()

But nothing of them worked. Do you have any idea what I am missing? I also noticed that when I delete the function you described:

public function handle($request, Closure $next)
{
    if ($request->id !== auth('api')->user->id) {
        abort(403);
    }

    return $next($request);
}

I still get the same error 401 (Unauthorized).

But when I encode my JWT, I pass user_id as the ID, does that have anything to do with it?

But in my database my user is declared as id like: $table->id();. Unfortunately, I am still too inexperienced to say if this could have something to do with the error message. I use the Tymon/jwt-auth package.

Thanks again a lot for your time and effort, I really appreciate it.


Solution

  • IMO, you should use /users routes in regards to users' CRUD and a /profile route for editing the user's data.

    Anyway, there is a simple way to achieve what you need with middlewares. Let's say that you want to allow to use a route ONLY if the {id} parameter is the same as the authenticated user.

    You can create a very basic middleware IE ResourceOwner

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    
    class ResourceOwner
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            $user = auth()->user();
            if ($request->id != $user->id) {
                abort(403);
            }
    
            return $next($request);
        }
    }
    

    The middleware will check if the incoming id parameter is the same as the user logged in, if not, abort throwing a 403 error response.

    In order to use this middleware, you can add it to your route

    use App\Http\Middleware\ResourceOwner;
    
    Route::get('user/{id}' ,'App\Http\Controllers\UserController@getUserById')->middleware(['auth:api', ResourceOwner::class]);