การใช้ Laravel Test 7 และ Laravel Passport 9.3 กับ Personal Access Client ให้ข้อยกเว้น“ การพยายามรับคุณสมบัติ 'id' ของ non-object”
ฉันกำลังออกแบบรูปแบบการตรวจสอบสิทธิ์ที่กำหนดเอง (ตามคีย์สาธารณะ) ควบคู่ไปกับ API แบบไร้สัญชาติและตัดสินใจว่า Passport จะตอบสนองความต้องการสำหรับคำขอหลังการตรวจสอบสิทธิ์
สมมติว่าการพิสูจน์ตัวตนสำเร็จและผู้ใช้ได้รับการพิสูจน์ตัวตนแล้วพวกเขาจะได้รับโทเค็นการเข้าถึงส่วนบุคคลและใช้โทเค็นสำหรับการร้องขอเพิ่มเติมทั้งหมด ปัญหาที่ฉันประสบ (หลังจากค้นหาผ่านฟอรัมต่างๆและ Stack Overflow เป็นจำนวนมาก) คือเมื่อใช้ชุดทดสอบในตัวของ Laravel บนเมธอด createToken () จะสร้างข้อยกเว้น (เป็นที่ยอมรับทั่วไป):
"ErrorException: พยายามรับคุณสมบัติ" id "ของ non-object"
ฉันสามารถสร้างผู้ใช้ด้วยตนเองผ่าน 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) เมื่อเรียกใช้โค้ดบรรทัดที่สามข้อยกเว้นจะถูกส่งด้วย mini stack-trace ต่อไปนี้ (ฉันสามารถจัดเตรียม stack-trace แบบเต็มได้หากมีการร้องขอ)
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
จากการเรียกใช้สิ่งนี้ผ่านการดีบักสองสามครั้งแม้ว่าคลาสจะถูกเรียกและโหลด แต่ดูเหมือนว่า Client จะถูกพบผ่าน ControllerDispatcher -> Client :: find (id) และพบใน ClientRepository เมื่อไปที่ PersonalAccessTokenFactory $client passed in is null (which explains why the $ไม่พบรหัสไคลเอ็นต์ -> แม้ว่าฉันไม่รู้ว่าทำไมไคลเอนต์ $ จึงเป็นโมฆะในตอนนี้)
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 ซึ่งใช้งานได้
- ตรวจสอบว่าผู้ใช้ล็อกอินก่อนที่จะพยายามสร้างโทเค็น
- หนังสือเดินทาง: ติดตั้ง (และเพิ่มตัวเลือก - บังคับ)
- ไคลเอ็นต์การเข้าถึงส่วนบุคคลที่ได้รับการรับรองถูกสร้างขึ้นด้วยหนังสือเดินทาง: ไคลเอนต์ - ส่วนบุคคล
- ตรวจสอบให้แน่ใจว่า AuthServiceProvider :: boot () มี ClientID และ Client Secret (ใน. env)
- โยกย้าย: รีเฟรชตามด้วยหนังสือเดินทาง: ติดตั้ง - บังคับ
- ลบ Passport โดยสมบูรณ์ลบไฟล์คีย์การย้ายข้อมูลและรายการ DB ทั้งหมดตามด้วยการโอนย้าย: รีเฟรชและติดตั้ง Passport ใหม่พร้อมกับสร้างไคลเอนต์การเข้าถึงส่วนบุคคลเพิ่มเติม (แม้ว่าจะสร้างขึ้นระหว่างพาสปอร์ต: ติดตั้ง)
ฉันไม่แน่ใจว่าจะดูที่ไหนดี / ต้องลองอะไรอีกบ้างในตอนนี้ดังนั้นความช่วยเหลือหรือคำแนะนำใด ๆ จะได้รับการชื่นชมมาก!
คำตอบ
ในที่สุดฉันก็ค้นพบวิธีแก้ปัญหา ปัญหานี้มีหลายชั้นซึ่งส่วนหนึ่งเกี่ยวข้องกับเอกสารของ Laravel ที่ล้าสมัยเกี่ยวกับการทดสอบและ Passport Personal Access Clients
ส่วนแรกของปัญหาเกี่ยวข้องกับการใช้ลักษณะ RefreshDatabase ในการทดสอบหน่วยของฉัน เนื่องจากสิ่งนี้จะสร้างฐานข้อมูลจำลองที่มีชุดข้อมูลว่างแม้ว่าไคลเอ็นต์จะมีอยู่ในฐานข้อมูลจริงและไฟล์. env เมื่อทำการทดสอบการทดสอบจะไม่เห็นไคลเอ็นต์เหล่านั้นว่ามีอยู่ในฐานข้อมูลจำลอง ในการแก้ปัญหานี้คุณต้องสร้างไคลเอนต์ในฟังก์ชันการตั้งค่าก่อนที่จะรันการทดสอบ
public function setUp() : void
{
parent::setUp();
$this->createClient(); //Private method->Full code below
}
วิธีนี้ช่วยแก้ปัญหาเกี่ยวกับการมีไคลเอนต์ว่างระหว่างการทดสอบ แต่เริ่มต้นใน Laravel 7 Laravel ได้เพิ่มข้อกำหนดสำหรับPersonal Access Clientsที่ต้องเก็บ id และความลับของไคลเอ็นต์ไว้ในไฟล์. env เมื่อเรียกใช้การทดสอบการทดสอบจะเห็นรหัสไคลเอ็นต์จริงและรหัสลับใน. env และไม่สามารถตรวจสอบความถูกต้องเหล่านี้กับไคลเอนต์ที่สร้างและเก็บไว้ในฐานข้อมูลจำลองส่งกลับข้อยกเว้นอื่น: "การตรวจสอบสิทธิ์ไคลเอ็นต์ล้มเหลว"
วิธีแก้ปัญหานี้คือสร้างไฟล์. env.testing ในไดเร็กทอรีโปรเจ็กต์หลักของคุณคัดลอกเนื้อหาไฟล์. env ของคุณไปที่ไฟล์และตรวจสอบให้แน่ใจว่าคีย์ด้านล่างมีอยู่พร้อมกับค่าสำหรับ Personal Access Client หลักที่สร้างขึ้นหรือคัดลอกข้อมูลลับ จากไคลเอนต์ที่สร้างขึ้นเพื่อการทดสอบเท่านั้น (ฉันขอแนะนำอย่างหลัง)
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();
}
สิ่งนี้จะสร้างไคลเอนต์ใหม่ตั้งค่าความลับของแอตทริบิวต์เป็นค่าในตัวแปรและอัปเดตข้อมูลลับฐานข้อมูลจำลองให้มีค่าเดียวกัน หวังว่านี่จะช่วยทุกคนที่มีปัญหาเดียวกัน