laravel / sanctum

Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.
https://laravel.com/docs/sanctum
MIT License
2.76k stars 298 forks source link

Issue with Auth ID Returning Incorrect User in Route #531

Closed mohammadgh1370 closed 1 month ago

mohammadgh1370 commented 3 months ago

Sanctum Version

3.3.3

Laravel Version

9.52.16

PHP Version

8.1.0

Database Driver & Version

No response

Description

Route Definition I have a route defined as follows:

Route::get('/{slug}', [ProductController::class, 'show'])->name('show');

This route does not have the auth middleware applied, but an authentication token is sent.

Controller The controller method is as follows:

public function show(int|string $slug, Request $request): JsonResponse
{
    $model = $this->repository
        ->where('slug', $slug)
        ->with([
            'log',
        ])
        ->firstOrFail();
    // ...
}

Relation in Model The log relation is defined in the model as follows:

public function log(): MorphOne
{
    return $this->morphOne(Enrollment::class, 'entity')
        ->where('user_id', Auth::id());
}

I am using Auth::id() to get the current authenticated user's data. However, Auth::id() is not working correctly and always returns the user with ID 1 from the users table.

Investigation in Guard.php Upon investigation, I found that in the Guard.php class, it retrieves the tokenable relation like this:

$accessToken->tokenable

The query generated is as follows:

array:2 [ // vendor/laravel/sanctum/src/Guard.php:72
  0 => array:3 [
    "query" => "select * from `personal_access_tokens` where `personal_access_tokens`.`id` = ? limit 1"
    "bindings" => array:1 [
      0 => "77217"
    ]
    "time" => 0.45
  ]
  1 => array:3 [
    "query" => "select * from `users` where `users`.`deleted_at` is null limit 1"
    "bindings" => []
    "time" => 0.29
  ]
]

Fix

I made the following change in the PersonalAccessToken model within the findToken method, which resolved the issue:

Instead of this:

$instance = static::find($id)

I used:

$instance = static::query()->with('tokenable')->find($id)

The resulting query was:

array:2 [ // vendor/laravel/sanctum/src/Guard.php:72
  0 => array:3 [
    "query" => "select * from `personal_access_tokens` where `personal_access_tokens`.`id` = ? limit 1"
    "bindings" => array:1 [
      0 => "77217"
    ]
    "time" => 0.43
  ]
  1 => array:3 [
    "query" => "select * from `users` where `users`.`id` in (55752) and `users`.`deleted_at` is null"
    "bindings" => []
    "time" => 0.48
  ]
]

This change fixed the issue, and now Auth::id() returns the correct user ID.

Steps To Reproduce

Steps To Reproduce

  1. Create a new Laravel project using laravel new bug-report.
  2. Add the following route to routes/web.php:
    Route::get('/{slug}', [ProductController::class, 'show'])->name('show');
  3. Create a ProductController with the following method:
    public function show(int|string $slug, Request $request): JsonResponse
    {
        $model = $this->repository
            ->where('slug', $slug)
            ->with([
                'log',
            ])
            ->firstOrFail();
        // ...
    }
  4. Define the log relationship in the model as follows:
    public function log(): MorphOne
    {
        return $this->morphOne(Enrollment::class, 'entity')
            ->where('user_id', Auth::id());
    }
  5. Send a request to the /show route with an authentication token.
  6. Observe that Auth::id() returns the user with ID 1 regardless of the actual authenticated user.
github-actions[bot] commented 3 months ago

Thank you for reporting this issue!

As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.

If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.

Thank you!

stevebauman commented 3 months ago

@mohammadgh1370 Please post your entire ProductController, as you're setting a repository property on it that we're not able to see which may be the source of your issue, as well as the repository itself. This is likely being set in your controllers constructor, which may be calling Auth::id() before authentication has actually been done on your application.

Also, your missing several steps in your "Steps to Reproduce", such as models, migrations, etc. Can you please create a replication repository with everything set up so it's immediately reproducible for us?

crynobone commented 1 month ago

Hey there,

We're closing this issue because it's inactive, already solved, old, or not relevant anymore. Feel free to open up a new issue if you're still experiencing this problem.