0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP GoogleClientを利用したOAuth認証でrefresh_tokenが取得できない場合の対応

Posted at

PHPのGoogleClientを利用して、OAuth認証を行う場合にリフレッシュトークンが取得できずエラーになる場合がありました。

※ログイン時、アクセストークンは取得できるので直後は問題ないが、1時間程度経過するとエラーになる

エラーメッセージの例

  • error: 'invalid_grant'
  • error_description: 'Bad Request'
  • refresh token must be passed in or set as part of setAccessToken

原因

  1. アクセストークンは初回ログイン時のみ入手できる(再度ログインしても取れない)
    • アクセストークンが取得できるのは、ログイン時に「スコープの確認」が表示されたときだけ
    • スコープの確認は2回目以降表示されないため、初回を逃すと正しいプログラムでもリフレッシュトークンは取れない
  2. アクセストークンを取得するには「AccessType: offline」かつ「ApprovalPrompt: force」である必要がある
  3. 初回に間違いのあるプログラムを実行して入手できないと、プログラムを修正した後も取得できない

対応

対応方法①

  • 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;
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?