Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assertCredentials is throwing an error #268

Closed
sebdesign opened this issue Apr 9, 2021 · 7 comments
Closed

assertCredentials is throwing an error #268

sebdesign opened this issue Apr 9, 2021 · 7 comments

Comments

@sebdesign
Copy link
Contributor

  • Sanctum Version: 2.9.1
  • Laravel Version: 8.32.1
  • PHP Version: 7.4.16
  • Database Driver & Version: MySQL 5.7

Description:

I'm using Sanctum for SPA authentication on an existing project, and while testing a new feature, I noticed that $this->assertCredentials() throws an error when accessing a route with the auth:sanctum middleware.

The reason of this error is that the default guard is sanctum, which doesn't have a provider by default.

   Error
  Call to a member function retrieveByCredentials() on null

  at vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php:147
    143protected function hasCredentials(array $credentials, $guard = null)
    144▕     {
    145$provider = $this->app->make('auth')->guard($guard)->getProvider();
    146▕
  ➜ 147$user = $provider->retrieveByCredentials($credentials);
    148149return $user && $provider->validateCredentials($user, $credentials);
    150▕     }
    151▕ }

  1   vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php:114
      Illuminate\Foundation\Testing\TestCase::hasCredentials()

  2   tests/Feature/Http/Controllers/Api/UsersControllerTest.php:52
      Illuminate\Foundation\Testing\TestCase::assertCredentials()

I've tried calling $this->actingAs($user, 'sanctum') and $this->assertCredentials([..], 'sanctum') and that still fails.
The only way that works is $this->assertCredentials([...], 'web').

Another way to fix this is to add this to config/auth.php:

'guards' => [
    'sanctum' => [
        'provider' => 'users',
    ],
],

I remember fixing some related issue in #225 , but that's still not enough.
In this comment #225 (comment) I mentioned that the root of the problem is #149, which is not implemented correctly. That implementation works when configuring custom guards, but not with a vanilla Sanctum installation.

Steps To Reproduce:

// routes/api.php
Route::resource('user', UsersController::class)->middleware('auth:sanctum');

// tests/Feature/UsersControllerTest.php

$response = $this->actingAs($user)
    ->handleValidationExceptions()
    ->postJson(route('user.store'), [
        'email' => '[email protected]',
        'password' => 'password',
        'password_confirmation' => 'password',
    ]);

$response->assertJsonMissingValidationErrors()->assertStatus(200);

$this->assertCredentials(['email' => '[email protected]', 'password' => 'password']);
@driesvints
Copy link
Member

Since Sanctum works token based, this method is incompatible with Sanctum.

@sebdesign
Copy link
Contributor Author

Sanctum is not only token-based, it relies on the user provider of the default guard for authenticating/fetching users for SPA. You can even specify a user provider in config/auth.php.

Even on token-based authentication, it checks the provider (if specified): https://github.com/laravel/sanctum/blob/2.x/src/Guard.php#L71

@driesvints
Copy link
Member

Heya, thanks for reporting.

I'll need more info and/or code to debug this further. Can you please create a repository with the command below, commit the code that reproduces the issue and share the repository here? Please make sure that you have the latest version of the Laravel installer in order to run this command. Please also make sure you have both Git & the GitHub CLI tool properly set up.

laravel new sanctum-issue-268 --github="--public"

After you've posted the repository, I'll try to reproduce the issue.

Thanks!

@driesvints driesvints reopened this Apr 13, 2021
@sebdesign
Copy link
Contributor Author

Hey @driesvints, I've created a repo for reproducing this issue: https://github.com/sebdesign/sanctum-issue-268

After the installation, I've installed Sanctum without any customization, in a separate commit.
The latest commit includes the routes and two tests, one using SPA authentication and one using API tokens.

I'm using an in-memory SQLite database, so after installing this you can run php artisan test.
Each test has many assertions for sanity checks, but the assertCredentials assertion that throws the error is at the end.

I hope that's not confusing and that you can understand the process. The key here is that the sanctum guard is missing something to make it work well with the underlying user provider.

@driesvints
Copy link
Member

I have to postpone looking into this a bit. I tried setting up the project locally but it causes PhpStorm for me to freeze and crash all the time. It's specific to this project and my colleagues aren't experiencing the same thing so it's specific to my device. I've contacted JetBrains in the hope to figure that out. Please do not remove your test repo for the time being so they can debug themselves.

In the meantime: I noticed you're sending multiple HTTP calls in a single test. This isn't officially support and can lead to edge cases with state management. You're also using an in-memory sqlite database which can be a culprit in combination with this.

Please try the following to see if that helps in resolving to the problem:

  • Writing separate tests for each HTTP call
  • Using a MySQL database instead

Please do not commit these changes to the test repo so JetBrains can use it in its current state to debug the crashing issue.

@sebdesign
Copy link
Contributor Author

Thanks for the explanation! I'm using Visual Studio code with Ubuntu 18.04 in WSL2, so I don't know what might cause this.

The only reason I'm doing all these HTTP calls is for sanity checks, as a way to describe a more realistic flow.
But in my own projects, I only to $this->actingAs($admin) and then $this->post('/api/users') once (also I'm using MySQL 5.7).

I don't believe this is a database issue, because the reason the user provider is null is because the sanctum guard does not have a user provider in the configuration or during the booting of its service provider.

@driesvints
Copy link
Member

I think I understand what's going on. It's not so much the sanctum guard that you need to target but the web guard. Try this:

        $this->assertCredentials([
            'email' => '[email protected]',
            'password' => 'password',
        ], 'web');

Your tests will pass. Also see notes like this in the docs:

Screenshot 2021-04-19 at 14 43 04

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants