Menggunakan Laravel Test 7 dan Laravel Passport 9.3 dengan Personal Access Client memberikan pengecualian "Mencoba mendapatkan properti 'id' non-objek"
Saya merancang skema otentikasi khusus (berdasarkan kunci publik) bersama dengan API tanpa negara, dan memutuskan Paspor akan memenuhi kebutuhan untuk permintaan otentikasi pasca.
Dengan asumsi otentikasi berhasil, dan pengguna diautentikasi, mereka akan menerima Token Akses Pribadi, dan menggunakan token untuk semua permintaan lebih lanjut. Masalah yang saya alami (masih setelah banyak mencari melalui berbagai forum dan Stack Overflow) adalah bahwa ketika menggunakan rangkaian pengujian bawaan Laravel, pada metode createToken (), itu menghasilkan pengecualian (yang diakui umum):
"ErrorException: Mencoba mendapatkan properti 'id' dari non-objek".
Saya dapat membuat pengguna secara manual melalui Tinker, dan membuat token melalui Tinker. Namun saya mengalami masalah saat mencoba mengotomatiskan proses ini setelah mengautentikasi .
Berikut ini cuplikan kode pasca-otentikasi yang relevan:
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);
Saya secara manual memanggil Auth :: login pada pengguna, untuk memastikan pengguna login, dan Auth :: user () mengembalikan pengguna (bukan null). Setelah menjalankan baris ketiga kode, pengecualian dilemparkan dengan pelacakan tumpukan mini berikut (saya dapat memberikan pelacakan tumpukan lengkap jika diminta).
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
Dari menjalankan ini melalui debug beberapa kali- meskipun kelas dipanggil dan dimuat, dan tampaknya Klien ditemukan melalui ControllerDispatcher -> Client :: find (id) dan ditemukan di ClientRepository, ketika masuk ke PersonalAccessTokenFactory, $client passed in is null (which explains why the $client-> id tidak dapat ditemukan, meskipun saya tidak tahu mengapa $ client nol pada saat ini).
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,
...
}
Hal-hal yang telah saya lakukan / coba dengan beberapa panduan dari dokumentasi dan posting lain:
- Membuat pengguna secara manual di Tinker, dan membuat token melalui Tinker- ini berfungsi .
- Pastikan pengguna masuk sebelum mencoba membuat token.
- paspor: instal (dan tambahkan opsi --force)
- Pastikan Akses Pribadi Klien dibuat dengan paspor: klien - pribadi
- Memastikan AuthServiceProvider :: boot () berisi ClientID dan Rahasia Klien (dalam .env).
- migrate: refresh diikuti dengan passport: install --force
- Penghapusan lengkap Paspor, menghapus semua file, kunci, migrasi, dan entri DB, diikuti dengan migrasi: menyegarkan dan menginstal ulang Paspor, bersama dengan menghasilkan klien akses pribadi tambahan (meskipun satu dibuat selama paspor: instal).
Saya tidak yakin ke mana lagi harus mencari / apa lagi yang harus dicoba saat ini, jadi bantuan atau panduan apa pun akan sangat kami hargai!
Jawaban
Saya akhirnya menemukan solusinya. Masalahnya berlapis-lapis, sebagian berkaitan dengan dokumentasi Laravel yang sudah ketinggalan zaman sehubungan dengan pengujian dan Klien Akses Pribadi Paspor.
Bagian pertama dari masalah ini berkaitan dengan penggunaan sifat RefreshDatabase pada pengujian unit saya. Karena ini membuat database tiruan dengan set data kosong, meskipun klien itu sendiri ada di database sebenarnya dan file .env, saat pengujian dijalankan, pengujian tidak melihat klien tersebut sebagai yang ada di database tiruan. Untuk mengatasi masalah ini, Anda harus membuat klien dalam fungsi penataan sebelum pengujian dijalankan.
public function setUp() : void
{
parent::setUp();
$this->createClient(); //Private method->Full code below
}
Ini memecahkan masalah tentang memiliki klien null selama pengujian, tetapi mulai di Laravel 7, Laravel menambahkan persyaratan untuk Klien Akses Pribadi bahwa id dan rahasia klien harus disimpan di dalam file .env. Saat menjalankan pengujian, pengujian akan melihat id dan rahasia klien yang sebenarnya di .env, dan gagal memvalidasinya dengan klien yang dibuat dan disimpan dalam database tiruan, mengembalikan pengecualian lain: "Autentikasi Klien Gagal".
Solusi untuk masalah ini adalah dengan membuat file .env.testing di direktori proyek utama Anda, menyalin konten file .env Anda ke dalamnya dan memastikan bahwa kunci di bawah ini ada dengan nilai untuk Personal Access Client utama yang Anda buat, atau menyalin rahasianya. dari klien yang dibuat hanya untuk pengujian (saya sarankan yang terakhir).
PASSPORT_PERSONAL_ACCESS_CLIENT_ID=1
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=unhashed-client-secret-value
Kemudian menggunakan kode di bawah ini, pastikan nilai $ clientSecret sama dengan nilai kunci di file .env.testing Anda.
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();
}
Ini akan membuat klien baru, menyetel rahasia atribut ke nilai dalam variabel, dan memperbarui rahasia database tiruan agar berisi nilai yang sama. Semoga ini membantu siapa pun dengan masalah yang sama.