パーソナルアクセスクライアントでLaravelTest7とLaravelPassport 9.3を使用すると、「非オブジェクトのプロパティ「id」を取得しようとしています」という例外が発生します。
ステートレスAPIと一緒に(公開鍵に基づく)カスタム認証スキームを設計しており、Passportが認証後の要求のニーズを満たすことを決定しました。
認証が成功し、ユーザーが認証されたとすると、ユーザーはパーソナルアクセストークンを受け取り、それ以降のすべてのリクエストにそのトークンを使用します。私が経験している問題は(さまざまなフォーラムやStack Overflowを何度も検索した後でも)、Laravelの組み込みテストスイートをcreateToken()メソッドで使用すると、(明らかに一般的な)例外が生成されることです。
「ErrorException:非オブジェクトのプロパティ「id」を取得しようとしています」。
Tinkerを使用して手動でユーザーを作成し、Tinkerを使用してトークンを作成することができます。ただし、認証後にこのプロセスを自動化しようとすると、問題が発生します。
認証後の関連するコードスニペットは次のとおりです。
Auth::login($user); $user = Auth::user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
$token->expires_at = Carbon::now()->addWeeks(1); $token->save();
return response()->json([
"access_token" => $tokenResult->accessToken, "token_type" => "Bearer", "expires_at" => Carbon::parse( $tokenResult->token->expires_at)->toDateTimeString()
],
200);
ユーザーがログインしていることを確認するために、ユーザーに対して手動でAuth :: loginを呼び出しました。Auth:: user()はユーザーを返します(nullではありません)。コードの3行目を実行すると、次のミニスタックトレースで例外がスローされます(要求された場合は完全なスタックトレースを提供できます)。
laravel\passport\src\PersonalAccessTokenFactory.php:100
laravel\passport\src\PersonalAccessTokenFactory.php:71
laravel\passport\src\HasApiTokens.php:67
app\Http\Controllers\Auth\LoginController.php:97
laravel\framework\src\Illuminate\Routing\Controller.php:54
laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php:45
これを数回実行してからデバッグまで-クラスが呼び出されてロードされ、クライアントがControllerDispatcher-> Client :: find(id)を介して検出され、ClientRepositoryで検出されたように見えても、PersonalAccessTokenFactoryに到達すると、 $client passed in is null (which explains why the $client-> idが見つかりませんが、この時点で$ clientがnullである理由はわかりません)。
protected function createRequest($client, $userId, array $scopes)
{
$secret = Passport::$hashesClientSecrets ? Passport::$personalAccessClientSecret : $client->secret;
return (new ServerRequest)->withParsedBody([
'grant_type' => 'personal_access',
'client_id' => $client->id,
...
}
ドキュメントや他の投稿からのガイダンスで私が行った/試したこと:
- Tinkerでユーザーを手動で作成し、Tinkerを介してトークンを作成しました-これは機能します。
- トークンを生成する前に、ユーザーがログインしていることを確認してください。
- passport:install(および--forceオプションの追加)
- パーソナルアクセスクライアントがpassport:client--personalで生成されていることを確認
- AuthServiceProvider :: boot()にClientIDとクライアントシークレット(.env内)が含まれていることを確認しました。
- 移行:更新、続いてパスポート:インストール--force
- Passportを完全に削除し、すべてのファイル、キー、移行、およびDBエントリを削除した後、passportのmigrate:refreshと再インストールを行い、追加のパーソナルアクセスクライアントを生成します(passport:install中に生成された場合でも)。
この時点で他にどこを探すべきか/他に何を試すべきかわからないので、助けやガイダンスをいただければ幸いです。
回答
私は最終的に解決策を発見しました。問題は多層的であり、テストとPassport Personal AccessClientに関する古いLaravelドキュメントに部分的に関係しています。
問題の最初の部分は、ユニットテストでトレイトRefreshDatabaseを使用することと関係がありました。これにより、空のデータセットを含むモックデータベースが作成されるため、クライアント自体は実際のデータベースと.envファイルに存在しますが、テストを実行すると、テストではそれらのクライアントがモックデータベースに存在するものとして認識されません。この問題を解決するには、テストを実行する前に、セットアップ関数でクライアントを作成する必要があります。
public function setUp() : void
{
parent::setUp();
$this->createClient(); //Private method->Full code below
}
これにより、テスト中にクライアントがnullになる問題が解決されますが、Laravel 7以降、Laravelは、IDとクライアントシークレットを.envファイル内に保持する必要があるというパーソナルアクセスクライアントの要件を追加しました。テストを実行すると、テストは.env内の実際のクライアントIDとシークレットを確認し、モックデータベースで作成および保存されたクライアントでこれらを検証できず、「クライアント認証に失敗しました」という別の例外が返されます。
この問題の解決策は、メインプロジェクトディレクトリに.env.testingファイルを作成し、.envファイルの内容をそのファイルにコピーして、作成したメインのパーソナルアクセスクライアントの値とともに以下のキーが存在することを確認するか、シークレットをコピーすることです。テスト用に生成されたクライアントから(後者をお勧めします)。
PASSPORT_PERSONAL_ACCESS_CLIENT_ID=1
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=unhashed-client-secret-value
次に、以下のコードを使用して、$ clientSecret値が.env.testingファイルのキー値と同じであることを確認します。
private function createClient() : void
{
$clientRepository = new ClientRepository(); $client = $clientRepository->createPersonalAccessClient( null, 'Test Personal Access Client', 'http://localhost' ); DB::table('oauth_personal_access_clients')->insert([ 'client_id' => $client->id,
'created_at' => new DateTime,
'updated_at' => new DateTime,
]);
$clientSecret = 'unhashed-client-secret-value'; $client->setSecretAttribute($clientSecret); $client->save();
}
これにより、新しいクライアントが作成され、属性シークレットが変数の値に設定され、モックデータベースシークレットが同じ値を含むように更新されます。うまくいけば、これは同じ問題を抱えている人に役立つでしょう。