Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kendrick's avatar

API-route vulnerability

If I have an API-route like:

Route::get('doctor/patients/{doctor}',  function (Doctor $doctor) { 
	return UserResource::collection($doctor->patients()->orderBy('created_at', 'desc')->take(20)); 
});

to take e.g. 20 patients a doctor has, and I am logged in as the doctor (id: 1), I can use the API-route to display the patients.

Component:

methods: {
   takePatients(){
       axios.get('/api/doctor/patients/' + this.doctor.id).then(response => this.patients = response.data.data);
   }
}  

Now, if I visit /api/doctor/patients/1 I get those patients in JSON format from the DB.

The problem is, if I change the 1 to 2 in the url, I get 20 patients in JSON format from the doctor with the (id: 2), still logged in as doctor (1).

What do I need to add, to restrict this behavior?

0 likes
29 replies
MichalOravec's avatar

Doctor has user_id?

If yes it could be like

Route::get('doctor/patients/{doctor}',  function ($id) { 
    $doctor = Doctor::where('user_id', Auth::id())->firstOrFail($id);

    return UserResource::collection($doctor->patients()->orderBy('created_at', 'desc')->take(20)); 
});
kendrick's avatar

@michaloravec - Yes, user_id is given.

Currently with your adjustments, I get No query results for model [App\\Models\\Doctor] 3

3 represents the id of the doctors_table, which I am also passing to the api route. For this doctor the user_id equals 6, which should be the Auth::id().

But I still get an exception of Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException

If I dd($doctor) in the Controller - it also gives me the correct Model, why would it throw the 404, then?

kendrick's avatar

@michaloravec - Now I get a 500 internal server for the api-route: SQLSTATE[42S22]: Column not found: 1054 Unknown column '3' in 'field list' (SQL: select `3` from `doctors` where `user_id` is null limit 1)

"exception": "Illuminate\\Database\\QueryException"

wingly's avatar

If you want to scope something to the authenticated user you shouldn't pass any id in the route. Further more seems like a "doctor" is actually a user why is it a separate model and not a normal user ?

kendrick's avatar

@wingly - Everyone is a user, but there are additional roles, like a doctor. Within the doctors table, I refer a user_id from the users_table.

How would I then scope the patients to the authenticated user (doctor), without passing any id in the route?

wingly's avatar

Exactly as you said the way to differentiate between different kind of users should be based on their role. The role can be a simple field into your users table or a more complicated role/permission system eg https://docs.spatie.be/laravel-permission/v3/introduction/. It all depends to your use case but seems to me that creating different models for each role will only lead to troubles

MichalOravec's avatar

This should work.

$doctor = Auth::user()->doctor()->findOrFail($id);
kendrick's avatar

@michaloravec - Thank you for the patience. Already tried it this way, because the doctor() method is given on the User.php Model. But it still gives me a 500, this is weird:

"message": "Call to a member function doctor() on null",
"exception": "Symfony\Component\Debug\Exception\FatalThrowableError",

I am also passing $doctor = auth()->user()->doctor; to the view, don't know why it won't work within the API route. What am I missing?

wingly's avatar

I am assuming that you are using another authentication guard for you api does this work Auth::guard('api')->user()

kendrick's avatar

I am actually not using the auth:api middleware.

I surrounded the api route with:


Route::get('doctor/patients/{doctor}',  function ($id) { 
    $doctor = Auth::user()->doctor()->findOrFail($id);

    return UserResource::collection($doctor->patients()->orderBy('created_at', 'desc')->take(20)); 
});
 

Still seeing:

"message": "Call to a member function doctor() on null",
"exception": "Symfony\Component\Debug\Exception\FatalThrowableError",
wingly's avatar

Just to make it clear is this route inside your web.php or you api.php route file ?

wingly's avatar

But why? It seems that it should be moved into the web.php you are misusing the api routes. In any case if you check you RouteServiceProvider file you probably gonna see that the api middleware is applied there

kendrick's avatar

@wingly - Sorry my fault. I read another conversation - where it obviously was not within the api.php file, but web.php and tried to adjust it, based on the advice.

Any other ideas, why a member function of the Auth::user() is not available within api.php?

wingly's avatar

Assuming that your route is inside web.php file and you have an authenticated user and you have a doctor relation in the User model then this should work

Route::get('doctor/patients',  function () { 
    $doctor = Auth::user()->doctor;

    return UserResource::collection($doctor->patients()->orderBy('created_at', 'desc')->take(20)); 
});
kendrick's avatar

@wingly - Yes, that would work, definitely. But I am trying to make the API-route work.

wingly's avatar

The routes in routes/api.php are stateless and are assigned the api middleware group. Except if i am missing something from your authentication logic this route should be in web.php

kendrick's avatar

@wingly - Use case: I am fetching the patients through the API, and optionally listening on a Broadcast Event to fetch them again, whenever the event is triggered.

Would that be also possible with the normal route?

wingly's avatar
wingly
Best Answer
Level 29

What do you mean by api if the only thing that you mean is that you are doing an ajax request and return a json response then yes it will work and it's the way to go if you mean that you have a complete setup with a token based authentication (eg JWT) then yes this belongs to api routes

kendrick's avatar

@wingly - Okay, and is there a way to prevent the JSON Format being displayed to the user when visiting doctor/patients?

wingly's avatar

You can check if the $request->expectsJson() else throw an abort(403) or whatever you can event extract it to a middleware an apply it where ever you want by all meanings this is not securing anything since there are ways to go around it

kendrick's avatar

@wingly - Yes, but now only patients of the authenticated user (doctor) will be shown, which is more secure, than being able to change an id in the url, and get patients of another doctor.

wingly's avatar

What i ment is that there is no security issue here as long as you don't return sensitive data. In any case everything that your return as json is visible to the end user so you shouldn't worry about that

kendrick's avatar

@wingly - Ok, is there any difference in performance between the normal return, and using a api-route?

wingly's avatar

No and i think this thread is answered now...

kendrick's avatar

Thank you for the advice and patience.

Please or to participate in this conversation.