개인 액세스 클라이언트와 함께 Laravel Test 7 및 Laravel Passport 9.3을 사용하면 "비 객체의 'id'속성을 얻으려고 시도하는 중"예외가 발생합니다.

Aug 19 2020

상태 비 저장 API와 함께 사용자 지정 인증 체계 (공개 키 기반)를 설계하고 있으며 Passport가 사후 인증 요청에 대한 요구를 충족시킬 것이라고 결정했습니다.

인증이 성공하고 사용자가 인증되었다고 가정하면 개인 액세스 토큰을 받고 모든 추가 요청에이 토큰을 사용합니다. 내가 경험하는 문제는 (여전히 다양한 포럼과 Stack Overflow를 통해 많은 검색 후에도) createToken () 메서드에서 Laravel의 내장 테스트 스위트를 사용할 때 (분명히 일반적인) 예외가 생성된다는 것입니다.

"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 아님)를 반환합니다. 코드의 세 번째 줄을 실행하면 다음과 같은 미니 스택 추적과 함께 예외가 발생합니다 (요청시 전체 스택 추적을 제공 할 수 있음).

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가 null 인 이유를 모르겠지만 client-> id를 찾을 수 없습니다.

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로 Personal Access Client가 생성되었는지 확인
  • AuthServiceProvider :: boot ()에 ClientID 및 Client Secret (.env)이 포함되어 있는지 확인했습니다.
  • migrate : refresh 다음에 passport : install --force
  • Passport를 완전히 제거하고 모든 파일, 키, 마이그레이션 및 DB 항목을 제거한 다음, 추가 개인 액세스 클라이언트를 생성하는 것과 함께 마이그레이션 : 새로 고침 및 Passport 재설치를 수행합니다 (passport : install 중에 하나가 생성 되더라도).

이 시점에서 다른 곳을 보거나 무엇을 시도해야할지 모르겠습니다. 어떤 도움이나 안내를 주시면 감사하겠습니다!

답변

grantley Aug 21 2020 at 22:41

나는 결국 해결책을 발견했습니다. 문제는 다층 적이며 부분적으로 테스트 및 Passport Personal Access Clients와 관련하여 오래된 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 파일 내용을 여기에 복사하고 아래의 키가 주 생성 된 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();
}

그러면 새 클라이언트가 생성되고 속성 비밀을 변수의 값으로 설정하고 동일한 값을 포함하도록 모의 데이터베이스 비밀을 업데이트합니다. 바라건대 이것은 동일한 문제를 가진 모든 사람에게 도움이되기를 바랍니다.