方法Illuminate \ Auth \ RequestGuard :: logout不存在Laravel Passport

时间:2019-09-05 22:47:47

标签: laravel oauth-2.0 phpunit laravel-passport

我使用Laravel Passport构建API时,我相应地删除了网络路由及其保护措施

如何测试用户注销?

这是我到目前为止所拥有的:

Logout Test

/**
 * Assert users can logout
 *
 * @return void
 */
public function test_logout()
{
    // $data->token_type = "Bearer"
    // $data->access_token = "Long string that is a valid token stripped out for brevety"
    $response = $this->json('POST', '/api/logout', [], [
         'Authorization' => $data->token_type . ' ' . $data->access_token
    ]);
    $response->assertStatus(200);
}

routes/api.php

Route::post('logout', 'Auth\LoginController@logout')->name('logout');
控制器方法使用AuthenticatesUsers特性,因此保留了默认功能
/**
 * Log the user out of the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function logout(Request $request)
{
    $this->guard()->logout();

    $request->session()->invalidate();

    return $this->loggedOut($request) ?: redirect('/');
}
  

错误方法Illuminate \ Auth \ RequestGuard :: logout不存在

Laravel Documentation讨论了颁发和刷新访问令牌,但没有涉及撤销它们或执行注销

注意:我正在使用密码授予令牌

注意2:撤消用户令牌无效

public function logout(Request $request)
{
    $request->user()->token()->revoke();
    return $this->loggedOut($request);
}

Test Fails on second assertion

public function test_logout()
{
    $response = $this->json('POST', '/api/logout', [], [
         'Authorization' => $data->token_type . ' ' . $data->access_token
    ]);
    $response->assertStatus(200); // Passes
    $check_request = $this->get('/api/user');
    $check_request->assertForbidden(); // Fails
}

给出需要身份验证的默认路由

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

响应状态码[200]不是禁止的状态码。

那是怎么回事?以及如何使用Passport测试用户注销?

预先感谢

1 个答案:

答案 0 :(得分:2)

正在撤销令牌。该测试无法正常工作,但原因并不明显。

在一个测试中发出多个请求时,在两个请求之间不会重置laravel应用程序的状态。身份验证管理器是laravel容器中的一个单例,它保留已解析身份验证防护的本地缓存。已解决的身份验证防护会保留已身份验证用户的本地缓存。

因此,您对api/logout端点的第一个请求将解析auth管理器,该解析器将解析api保护,该保护将存储对您将要撤消其令牌的已认证用户的引用。

现在,当您向/api/user发出第二个请求时,已解决的auth管理器将从容器中拉出,已解决的api Guard从其本地缓存中拉出,而同一已解决的用户也将从中拉出守卫的本地缓存。这就是第二个请求通过身份验证而不是失败的原因。

在同一测试中测试具有多个请求的auth相关内容时,您需要在测试之间重置已解决的实例。另外,您不能仅取消设置已解析的身份验证管理器实例,因为当再次解析它时,将不会定义扩展的passport驱动程序。

因此,我找到的最简单的方法是使用反射在已解析的身份验证管理器上取消设置受保护的guards属性。您还需要在已解析的会话防护上调用logout方法。

我的TestCase类上有一个类似于以下内容的方法:

protected function resetAuth(array $guards = null)
{
    $guards = $guards ?: array_keys(config('auth.guards'));

    foreach ($guards as $guard) {
        $guard = $this->app['auth']->guard($guard);

        if ($guard instanceof \Illuminate\Auth\SessionGuard) {
            $guard->logout();
        }
    }

    $protectedProperty = new \ReflectionProperty($this->app['auth'], 'guards');
    $protectedProperty->setAccessible(true);
    $protectedProperty->setValue($this->app['auth'], []);
}

现在,您的测试将类似于:

public function test_logout()
{
    $response = $this->json('POST', '/api/logout', [], [
         'Authorization' => $data->token_type . ' ' . $data->access_token
    ]);
    $response->assertStatus(200);

    // Directly assert the api user's token was revoked.
    $this->assertTrue($this->app['auth']->guard('api')->user()->token()->revoked);

    $this->resetAuth();

    // Assert using the revoked token for the next request won't work.
    $response = $this->json('GET', '/api/user', [], [
         'Authorization' => $data->token_type . ' ' . $data->access_token
    ]);
    $response->assertStatus(401);
}