通过 Laravel Dusk 测试模拟 Socialite 登录时出现 InvalidStateException
InvalidStateException when testing mocked Socialite login via Laravel Dusk
我在 Laravel 应用程序中使用 Socialite 以允许用户通过 Github 进行连接。
我的登录控制器包含以下两个方法:
/**
* GET /login/github
* Redirect the user to the GitHub authentication page.
*/
public function redirectToProvider()
{
return Socialite::driver('github')->redirect();
}
/**
* GET /login/github/callback
* Obtain the user information from GitHub.
*/
public function handleProviderCallback(Request $request)
{
$githubUser = Socialite::driver('github')->user();
// Actual login procedures go here; redacted for brevity
return redirect('/');
}
当我在浏览器中手动测试这些方法时,它们按预期工作。我访问 /login/github
,在那里我被重定向到 Github 进行身份验证,然后我被送回 /login/github/callback?state=somelongrandomkey
,然后我被重定向到主页 (/
)。
我也在尝试通过 Laravel Dusk 来测试这些方法,嘲笑社会名流。
我的 Dusk 测试方法如下所示:
public function testReceivesGithubRequestAndCreatesNewUser()
{
$this->browse(function (Browser $browser) {
$user = factory('App\Models\User')->create([
'github_token' => 'foobar',
'github_username' => 'foobar'
]);
# Mock 1 - A Socialite user
$abstractUser = Mockery::mock('Laravel\Socialite\Two\User');
# Mock 2 - Socialite's Github provider
$provider = Mockery::mock('Laravel\Socialite\Contracts\Provider');
$provider->shouldReceive('user')
->andReturn($abstractUser);
# Mock 3 - Socialite
Socialite::shouldReceive('driver')
->with('github')
->andReturn($provider);
$browser->visit('/login/github/callback')->assertPathIs('/');
});
当我 运行 这个测试时,对 /login/github/callback
的访问失败并显示 InvalidStateException
。
来自日志:
dusk.local.ERROR: {"exception":"[object] (Laravel\Socialite\Two\InvalidStateException(code: 0): at /redacted/vendor/laravel/socialite/src/Two/AbstractProvider.php:210)
[stacktrace]
#0 /redacted/app/Http/Controllers/Auth/LoginController.php(84): Laravel\Socialite\Two\AbstractProvider->user()
[...etc...]
当我trace where the error is coming from in AbstractProvider
我看到它正在尝试将会话中的 state
与查询字符串中的 state
进行比较:
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$state = $this->request->session()->pull('state');
return ! (strlen($state) > 0 && $this->request->input('state') === $state);
}
在我的 Dusk 测试中,当访问 /login/github/callback
时,查询字符串上没有任何状态,因此失败是合乎逻辑的。
我觉得我在设置提供该状态的模拟时缺少一些关键组件,但我不确定是什么。
我的测试是使用这两个示例构建的以供参考:
Dusk 测试与您提到的示例之间存在根本区别:Dusk 在实际浏览器中打开网站,因此测试和您的应用程序 运行 在不同的进程中。这就是模拟在 Dusk 测试中不起作用的原因。
这种集成测试背后的想法是模拟真实用户,无需模拟或任何其他 "shortcuts"。在您的情况下,这意味着使用真实的 GitHub 帐户登录。
我在 Laravel 应用程序中使用 Socialite 以允许用户通过 Github 进行连接。
我的登录控制器包含以下两个方法:
/**
* GET /login/github
* Redirect the user to the GitHub authentication page.
*/
public function redirectToProvider()
{
return Socialite::driver('github')->redirect();
}
/**
* GET /login/github/callback
* Obtain the user information from GitHub.
*/
public function handleProviderCallback(Request $request)
{
$githubUser = Socialite::driver('github')->user();
// Actual login procedures go here; redacted for brevity
return redirect('/');
}
当我在浏览器中手动测试这些方法时,它们按预期工作。我访问 /login/github
,在那里我被重定向到 Github 进行身份验证,然后我被送回 /login/github/callback?state=somelongrandomkey
,然后我被重定向到主页 (/
)。
我也在尝试通过 Laravel Dusk 来测试这些方法,嘲笑社会名流。
我的 Dusk 测试方法如下所示:
public function testReceivesGithubRequestAndCreatesNewUser()
{
$this->browse(function (Browser $browser) {
$user = factory('App\Models\User')->create([
'github_token' => 'foobar',
'github_username' => 'foobar'
]);
# Mock 1 - A Socialite user
$abstractUser = Mockery::mock('Laravel\Socialite\Two\User');
# Mock 2 - Socialite's Github provider
$provider = Mockery::mock('Laravel\Socialite\Contracts\Provider');
$provider->shouldReceive('user')
->andReturn($abstractUser);
# Mock 3 - Socialite
Socialite::shouldReceive('driver')
->with('github')
->andReturn($provider);
$browser->visit('/login/github/callback')->assertPathIs('/');
});
当我 运行 这个测试时,对 /login/github/callback
的访问失败并显示 InvalidStateException
。
来自日志:
dusk.local.ERROR: {"exception":"[object] (Laravel\Socialite\Two\InvalidStateException(code: 0): at /redacted/vendor/laravel/socialite/src/Two/AbstractProvider.php:210)
[stacktrace]
#0 /redacted/app/Http/Controllers/Auth/LoginController.php(84): Laravel\Socialite\Two\AbstractProvider->user()
[...etc...]
当我trace where the error is coming from in AbstractProvider
我看到它正在尝试将会话中的 state
与查询字符串中的 state
进行比较:
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$state = $this->request->session()->pull('state');
return ! (strlen($state) > 0 && $this->request->input('state') === $state);
}
在我的 Dusk 测试中,当访问 /login/github/callback
时,查询字符串上没有任何状态,因此失败是合乎逻辑的。
我觉得我在设置提供该状态的模拟时缺少一些关键组件,但我不确定是什么。
我的测试是使用这两个示例构建的以供参考:
Dusk 测试与您提到的示例之间存在根本区别:Dusk 在实际浏览器中打开网站,因此测试和您的应用程序 运行 在不同的进程中。这就是模拟在 Dusk 测试中不起作用的原因。
这种集成测试背后的想法是模拟真实用户,无需模拟或任何其他 "shortcuts"。在您的情况下,这意味着使用真实的 GitHub 帐户登录。