web-dev-qa-db-ja.com

Google API Client「リフレッシュトークンを渡すか、setAccessTokenの一部として設定する必要があります」

私は現在非常に奇妙な問題に直面しています。実際、Google APIのこのガイド( https://developers.google.com/google-apps/calendar/quickstart/php )に従っています。ドキュメンテーション。私は2回試しましたが、最初は魅力のように機能しましたが、アクセストークンの有効期限が切れた後、Google API Docによって直接提供されたスクリプトはそれを更新できませんでした。

TL; DR

エラーメッセージは次のとおりです。

sam@ssh:~$ php www/path/to/app/public/quickstart.php


Fatal error: Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php:258
Stack trace:
#0 /home/pueblo/www/path/to/app/public/quickstart.php(55): Google_Client->fetchAccessTokenWithRefreshToken(NULL)
#1 /home/pueblo/www/path/to/app/public/quickstart.php(76): getClient()
#2 {main}
  thrown in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php on line 258

これが、私が変更したgoogleのphpスクリプトの一部です。

require_once __DIR__ . '/../vendor/autoload.php';

// I don't want the creds to be in my home folder, I prefer them in the app's root
define('APPLICATION_NAME', 'LRS API Calendar');
define('CREDENTIALS_PATH', __DIR__ . '/../.credentials/calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/../client_secret.json');

expandHomeDirectoryも変更したので、あまり多くのコードを変更せずに「無効化」できました。

function expandHomeDirectory($path) {
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) {
    $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
  }
  return $path;
  // return str_replace('~', realpath($homeDirectory), $path);
}

だから私が間違っていたのか、Googleがそうだったのかを確認するために、実験を行いました:昨日夜、sshからクイックスタートスクリプトを起動して、それが機能しているかどうかを確認しました寝る前とそうではなかったので、グーグルのquickstart.php

誰かが私を助けてくれることを願っています、私はすでに主題に関する他のすべての投稿をチェックしましたが、それらはすべて時代遅れです。

14
Sam

私は最近同じ問題を抱え、これで解決しました。

<?php
 $client->setRedirectUri($this->_redirectURI);
 $client->setAccessType('offline');
 $client->setApprovalPrompt('force');

私は説明します...オフラインモードでは十分ではありません。私たちは承認プロンプトを強制する必要があります。また、これら2つのオプションの前にredirectURIを設定する必要があります。それは私のために働いた。

これは私の完全な機能です

<?php
     private function getClient()
     {
        $client = new Google_Client();
        $client->setApplicationName($this->projectName);
        $client->setScopes(SCOPES);
        $client->setAuthConfig($this->jsonKeyFilePath);
        $client->setRedirectUri($this->redirectUri);
        $client->setAccessType('offline');
        $client->setApprovalPrompt('force');

       // Load previously authorized credentials from a file.
       if (file_exists($this->tokenFile)) {
         $accessToken = json_decode(file_get_contents($this->tokenFile), 
         true);
      } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));

        if (isset($_GET['code'])) {
            $authCode = $_GET['code'];
            // Exchange authorization code for an access token.
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            header('Location: ' . filter_var($this->redirectUri, 
            FILTER_SANITIZE_URL));
            if(!file_exists(dirname($this->tokenFile))) {
                mkdir(dirname($this->tokenFile), 0700, true);
            }

            file_put_contents($this->tokenFile, json_encode($accessToken));
        }else{
            exit('No code found');
        }
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {

        // save refresh token to some variable
        $refreshTokenSaved = $client->getRefreshToken();

        // update access token
        $client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

        // pass access token to some variable
        $accessTokenUpdated = $client->getAccessToken();

        // append refresh token
        $accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

        //Set the new acces token
        $accessToken = $refreshTokenSaved;
        $client->setAccessToken($accessToken);

        // save to file
        file_put_contents($this->tokenFile, 
       json_encode($accessTokenUpdated));
    }
    return $client;
}
18
Ulrich Dohou

新しいGoogle APIライブラリでも同じ問題に直面しました。ソリューションを検索すると次のリンクが表示されました: RefreshTokenが新しいトークンのGoogleシートAPIを取得した後に返送されません

その情報に基づいて、クイックスタートコードの部分を自分のニーズに合わせて変更しました。 Googleでの最初の承認後、3600秒または1時間で期限切れになるrefresh_tokenを含むdrive-php-quickstart.jsonを取得しました。リフレッシュトークンは1回だけ発行されるため、紛失した場合は再認証が必要です。したがって、drive-php-quickstart.jsonに常に保存するために、以下を実行しました。

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken(); 

// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved); 

// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();

// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

// save to file
file_put_contents($credentialsPath, json_encode($accessTokenUpdated)); 
}
4

私のアドバイスは、アクセストークンの取得直後にリフレッシュトークンを.jsonに保存で、アクセストークンの有効期限が切れた場合は、リフレッシュトークンを使用します。

私のプロジェクトでは次のように動作します:

public static function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName('JhvInformationTable');
    $client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
    $client->setAuthConfig('credentials.json');
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = 'token.json';
    $credentialsPath2 = 'refreshToken.json';
    if (file_exists($credentialsPath)) {
        $accessToken = json_decode(file_get_contents($credentialsPath), true);
    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        //printf("Open the following link in your browser:\n%s\n", $authUrl);
        //print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        //echo "<script> location.href='".$authUrl."'; </script>";
        //exit;

        $authCode ='********To get code, please uncomment the code above********';

        // Exchange authorization code for an access token.
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
        $refreshToken = $client->getRefreshToken();

        // Check to see if there was an error.
        if (array_key_exists('error', $accessToken)) {
            throw new Exception(join(', ', $accessToken));
        }

        // Store the credentials to disk.
        if (!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        file_put_contents($credentialsPath, json_encode($accessToken));
        file_put_contents($credentialsPath2, json_encode($refreshToken));
        printf("Credentials saved to %s\n", $credentialsPath);
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $refreshToken = json_decode(file_get_contents($credentialsPath2), true);
        $client->fetchAccessTokenWithRefreshToken($refreshToken);
        file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
    }
    return $client;
}
4
Michal Blažek

このメッセージに問題がある人のためのいくつかの更新、主に最初のコマンドfetchAccessTokenWithAuthCode()のみがリフレッシュトークンを含むクレデンシャルを生成するためです(技術的に永久に有効-取り消さない場合は2時間有効ではありません)。新しいトークンを取得すると、元のトークンは置き換えられますが、必要なリフレッシュトークンが含まれていないため、次回トークンを更新する必要があるときにクラッシュします。これは、リフレッシュ機能を次のように置き換えることで簡単に修正できます。

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) {
    $oldAccessToken=$client->getAccessToken();
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    $accessToken=$client->getAccessToken();
    $accessToken['refresh_token']=$oldAccessToken['refresh_token'];
    file_put_contents($credentialsPath, json_encode($accessToken));
}

これで、アクセストークンを更新するたびに、リフレッシュトークンも渡されます。

2
Therian

私は同じ問題を抱えていて、最終的にこれを機能させます:

バックストーリー:

同じエラーを受け取りました。ここに私が見つけたものがあります:

このエラー:

PHP致命的エラー:キャッチされないLogicException:更新トークンを渡すか、/ Library/WebServer/Documents/Sites/test/scriptsのsetAccessTokenの一部として設定する必要があります/vendor/google/apiclient/src/Google/Client.php:267

更新アクセストークン(別名、更新)メソッドを参照しています:

$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

なぜ失敗したのですか?簡単に言えば、このjsonファイルのデコードから得られた$ accessToken配列を印刷したときに気づきました(投稿したクイックスタートコード/ Googleから得たもの)

credentials/calendar-php-quickstart.json

Print_rを実行したときにaccessToken配列がどのように出力されるかにより、エラーが見つかりました。

Array[access_token] => Array([access_token] => xyz123 [token_type] => Bearer [expires_in] => 3600 [refresh_token] => xsss112222 [created] => 1511379484)

解決策:

$ refreshToken = $ accessToken ["access_token"] ["refresh_token"];

この行の直前:

    $client->fetchAccessTokenWithRefreshToken($refreshToken);

トークンが1時間で期限切れになると、必要に応じて最終的にトークンを更新できます。この記事の開発者は、配列が次のように印刷されると仮定しました。

配列([access_token]=> xyz123 [token_type] => Bearer [expires_in] => 3600 [refresh_token] => xsss112222 [created] => 1511379484 )

だから彼らはあなたが単に$ accessToken ["refresh_token"];できると思った。それは正しくありません。

これで$ refreshTokenの有効な値が得られたので、これを行うとエラーは消えます。また、他のphp開発者がこの問題に遭遇した場合に備えて、フィードバックリンクを介して作成者を更新して、このことを知らせました。これが誰かの助けになることを願っています。この投稿のフォーマットが不十分な場合、申し訳ありませんが、私はS.Eを初めて使用します。これがついに機能するようになったので、共有したかっただけです。

1
user3760763

accestokenに書き込むときに、credentialsPathをシリアル化する必要があります。

 // Exchange authorization code for an access token.
    $accessToken = $client->authenticate($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) {
        mkdir(dirname($credentialsPath), 0700, true);
    }
    $serArray = serialize($accessToken);
    file_put_contents($credentialsPath, $serArray);
    printf("Credentials saved to %s\n", $credentialsPath);

また、ファイルから読み取るときは、シリアル化を解除する必要があります。

if (file_exists($credentialsPath)) {
    $unserArray =  file_get_contents($credentialsPath);
    $accessToken = unserialize($unserArray);

}

全機能

function getClient() {
    $client = new Google_Client();
    // Set to name/location of your client_secrets.json file.
    $client->setAuthConfigFile('client_secret.json');
    // Set to valid redirect URI for your project.
    $client->setRedirectUri('http://localhost');
    $client->setApprovalPrompt('force');

    $client->addScope(Google_Service_YouTube::YOUTUBE_READONLY);
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);


    if (file_exists($credentialsPath)) {
        $unserArray =  file_get_contents($credentialsPath);
        $accessToken = unserialize($unserArray);

    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        printf("Open the following link in your browser:\n%s\n", $authUrl);
        print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        // Exchange authorization code for an access token.
        $accessToken = $client->authenticate($authCode);

        // Store the credentials to disk.
        if(!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        $serArray = serialize($accessToken);
        file_put_contents($credentialsPath, $serArray);
        printf("Credentials saved to %s\n", $credentialsPath);
    }

    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $client->refreshToken($client->getRefreshToken());
        file_put_contents($credentialsPath, $client->getAccessToken());
    }
    return $client;
}
1

このコードをしばらく見てから:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

この変更のみが必要でした:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($accessToken);
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

この関数$ client-> getRefreshToken()はnullを返すため、$ accessTokenを直接指定すると、正常に機能してファイルが更新され、誰かの問題が解決することを願っています。

0
oron abutbul

Googleは PHPクイックスタート を更新し、これを処理する方法を改善しました。

以下のスニペット:

// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
dxdc

私の場合、更新トークンを生成せずにアクセスタイプを「オフライン」に設定するのを忘れていました。

$client->setAccessType('offline');

これが完了すると、Googleのドキュメントに記載されているサンプルコードが機能します。

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
jahackbeth