我正在使用Laravel 5.7和Passport为第一方客户机创建一个API。我有一个登录表单,它接受用户的电子邮件和密码,并将两者发送到自定义登录控制器。然后,LoginController创建一个oAuth负载,发送一个
POST
请求
oauth/token
通过Guzzle将访问令牌、刷新令牌和其他所有内容返回给我的第一方客户端。
这是我的对应代码:
登录控制器
<?php
namespace App\Http\Controllers\Api;
use App\Domain\Auth\PasswordGrant;
use App\Http\Requests\LoginRequest;
class LoginController extends ApiController
{
/**
* LoginController constructor.
*/
public function __construct()
{
$this->middleware('api')->only('login');
}
/**
* Attempt to authenticate the user with the credentials they provided
* and if successful, return an access token for the user.
*
* @param LoginRequest $request
* @return \Illuminate\Http\Response
*/
public function login(LoginRequest $request)
{
return PasswordGrant::attempt($request->email, $request->password);
}
}
<?php
namespace App\Domain\Auth;
use GuzzleHttp\Client as GuzzleHttp;
use GuzzleHttp\Exception\ClientException;
use Laravel\Passport\Client;
class PasswordGrant
{
/**
* The GuzzleHttp client instance.
*
* @var GuzzleHttp
*/
protected $http;
/**
* PasswordGrant constructor.
*
* @param GuzzleHttp $http
*/
public function __construct(GuzzleHttp $http)
{
$this->http = $http;
}
/**
* @param $username
* @param $password
* @return \Illuminate\Http\Response
*/
public static function attempt($username, $password)
{
$passwordGrant = resolve(static::class);
$payload = $passwordGrant->oAuthPayload(
$passwordGrant->oAuthClient(), $username, $password
);
return $passwordGrant->oAuthResponse($payload);
}
/**
* Get the oAuth Client we are using to authenticate our login and user.
*
* @return Client
*/
protected function oAuthClient()
{
return Client::query()
->where('name', config('api.password_client'))
->where('password_client', true)
->where('revoked', false)
->firstOrFail();
}
/**
* The payload we need to send to our oAuth server in order to receive
* a bearer token and authenticate the user.
*
* @param Client $client
* @param $username
* @param $password
* @return array
*/
protected function oAuthPayload(Client $client, $username, $password)
{
return [
'form_params' => [
'grant_type' => 'password',
'client_id' => $client->id,
'client_secret' => $client->secret,
'username' => $username,
'password' => $password,
'scope' => '*'
]
];
}
/**
* Get the response from our oAuth server.
*
* @param array $payload
* @return \Illuminate\Http\Response
*/
protected function oAuthResponse(array $payload)
{
try {
return $this->http->post(route('passport.token'), $payload)->getBody();
} catch (ClientException $exception) {
return response($exception->getMessage(), $exception->getCode());
}
}
}
密码格兰特
<?php
namespace Tests\Feature\Requests\Team;
use App\Domain\Auth\PasswordGrant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Tests\TestCases\TestCase;
class PasswordGrantTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function it_returns_an_access_token_for_a_user_with_valid_credentials()
{
Artisan::call('passport:client', [
'--password' => true,
'--name' => config('api.password_client')
]);
$user = create(User::class);
$result = PasswordGrant::attempt($user->email, 'secret');
dd($result);
}
}
dd
在测试结束时,总是返回401,并显示消息:
{"error":"invalid_client","message":"Client authentication failed"}
我已经三次检查了我的用户模型passport客户端的存在性和有效性,并确保有效负载的格式正确。
可能我在测试期间对服务器的请求中缺少某些头?