PHPのGoogleClientを利用して、OAuth認証を行う場合にリフレッシュトークンが取得できずエラーになる場合がありました。
※ログイン時、アクセストークンは取得できるので直後は問題ないが、1時間程度経過するとエラーになる
エラーメッセージの例
- error: 'invalid_grant'
- error_description: 'Bad Request'
- refresh token must be passed in or set as part of setAccessToken
原因
- アクセストークンは初回ログイン時のみ入手できる(再度ログインしても取れない)
- アクセストークンが取得できるのは、ログイン時に「スコープの確認」が表示されたときだけ
- スコープの確認は2回目以降表示されないため、初回を逃すと正しいプログラムでもリフレッシュトークンは取れない
- アクセストークンを取得するには「AccessType: offline」かつ「ApprovalPrompt: force」である必要がある
- 初回に間違いのあるプログラムを実行して入手できないと、プログラムを修正した後も取得できない
対応
対応方法①
- Googleからログオフして再度ログインが必要な状態にすればよい
※ログオフしても、ブラウザ(Chrome)側がログオン状態だとうまくいかない場合があったので、両方ログオフしてく
Chrome からログアウトする
対応方法②
- ログオン時、毎回「スコープの確認」を表示させる
// 同意画面を毎回表示する(初回ログイン時以外でもリフレッシュトークンを取得できるようにする)
$client->setPrompt('consent');
サンプルコード
GoogleClientの生成
public function getClient($access_token='', $refresh_token='')
{
$client = new Google_Client();
// 利用する機能(GCP側の設定(同意画面のスコープ)も必要)
$scope = [
Google_Service_Sheets::SPREADSHEETS,
];
// 接続に必要な情報をセット
$client->setClientId(self::GOOGLE_CLIENT_ID);
$client->setClientSecret(self::GOOGLE_CLIENT_SECRET);
$client->setRedirectUri(self::AUTH_CALLBACK_URL);
$client->addScope($scope);
$client->setAccessType('offline');
$client->setApprovalPrompt('force'); // これがないとリフレッシュトークンが取得できない
// 同意画面を毎回表示する(初回ログイン時以外でもリフレッシュトークンを取得できるようにする)
$client->setPrompt('consent');
if (!empty($access_token))
{
// ログイン前はtokenが空の状態で、ログイン画面にリダイレクト
// ログインから戻ったタイミングで、tokenが取得できる(ので、それを保存する)
// ログイン後は、保存したtokenを引数にセットすればCalenderやSpreadのAPIを呼ぶことができる
$client->setAccessToken($access_token);
// アクセストークンは60分で期限切れになるので、リフレッシュを行う
if ( $client->isAccessTokenExpired() ) {
$new_access_token = $client->fetchAccessTokenWithRefreshToken($refresh_token);
$client->setAccessToken($new_access_token);
}
}
return $client;
}
ログイン
// googleAPIライブラリ生成
$client = $this->MemberGooglePage->getClient();
// Googleログインページへリダイレクト
$auth_url = $client->createAuthUrl();
$this->redirect($auth_url);
ログイン後のcallback
$client = $this->getClient();
// auth_codeを元にアクセストークンを生成
$accessToken = $client->fetchAccessTokenWithAuthCode($auth_code);
// tokenの検証(ユーザー情報を取得)
$userInfo = $client->verifyIdToken($accessToken['id_token']);
// ログイン画面でセキュリティーの同意が表示された場合のみ、RefreshTokenが取得できる
// ・デフォルトでは、セキュリティーの同意は初回ログイン時のみ表示
$refresh_token = $client->getRefreshToken();
取得したトークンの使い方
// googleClientに、tokenをセット
$refresh_token = <DBやファイルに保存した値を読み込む>
$access_token = <DBやファイルに保存した値を読み込む>
$client = $this->getClient($access_token, $refresh_token);
// スプレッドシートを新規に作成
$service = new Google_Service_Sheets($client);
$requestBody = new Google_Service_Sheets_Spreadsheet([
'properties' => [
'title' => 'スプレッドシートタイトル'
]
]);
$response = $service->spreadsheets->create($requestBody);
// 新規作成したスプレッドシートidを返す
$spreadsheet_id = $response->spreadsheetId;