0

Been racking my brain with this all day. Not sure why I'm seeing this error in production but no error when testing locally.

{
    "message": "refresh token must be passed in or set as part of setAccessToken",
    "exception": "LogicException",
    "file": "/var/www/laravel/vendor/google/apiclient/src/Google/Client.php",
    "line": 266,
    "trace": [
        {
            "file": "/var/www/laravel/vendor/google/apiclient/src/Google/Client.php",
            "line": 254,
            "function": "fetchAccessTokenWithRefreshToken",
            "class": "Google_Client",
            "type": "->"
        },
        {
            "file": "/var/www/laravel/app/Services/GoogleService.php",
            "line": 50,
            "function": "refreshToken",
            "class": "Google_Client",
            "type": "->"

       }
}

My setup is as follow:

class GoogleService {

    protected $client;

    protected $service;

    function __construct() {
        /* Get config variables */
        putenv('GOOGLE_APPLICATION_CREDENTIALS=' . resource_path(config('google.credentials')));

        $scopes = array(
            ...
        );

        $this->client = new \Google_Client();
        $this->client->useApplicationDefaultCredentials();

        $this->client->setApplicationName(config('google.app_name'));

        $service_account = config('google.service_account_name');
        $this->client->setSubject($service_account);

        $this->client->setScopes($scopes);
        $this->client->setAccessType('offline');
        $this->client->setApprovalPrompt('force');

        $this->service = new \Google_Service_Directory($this->client);

        /* If we have an access token */
        if (Cache::has('service_token')) {
            $this->client->setAccessToken(Cache::get('service_token'));
        }

        if ($this->client->isAccessTokenExpired()) {
            $this->client->refreshToken(Cache::get('service_token'));
        }

        Cache::forever('service_token', $this->client->getAccessToken());
    }

    public function verify($user_id)
    {
        try {

            $result = $this->service->users->get($user_id);

        } catch (\Google_Service_Exception $e) {

            Log::error($e->getMessage());

            return response()->json(['error' => 'There was a general error : ' . $e->getMessage()], 404);

        }

        return $result;
    }

    public function get($user_id)
    {
        $optParams = array('projection' => 'full');
        $user = $this->service->users->get($user_id, $optParams);

        if ($user_id !== $user->primaryEmail) {
            return response()->json(['error' => 'Unauthorized - Bad credentials.'], 401);
        }

        return $user;
    }
}

I'm finally done working locally using vagrant/homestead on my mac and I try to load to production and am unable to run this. I've found some other posts about errors but it seems to only be for client to server and in this instance is server to server. I wonder if that makes a difference.

Any help would be appreciated.

Thanks all!

sogeniusio
  • 111
  • 3
  • 14

2 Answers2

0

The first time you run this code, service_token will not exist in the cache, so

  1. Cache::has('service_token') will return false, and
  2. Cache::get('service_token') will return null.

In this code:

/* If we have an access token */
if (Cache::has('service_token')) { // false
    $this->client->setAccessToken(Cache::get('service_token'));
}

if ($this->client->isAccessTokenExpired()) { // true
    $this->client->refreshToken(Cache::get('service_token'));
}

The first condition will be false, so the setAccesstoken() will not get called. The second condition will return true, because isAccessTokenExpired() returns true when the token is not set, so you'll be passing in null to the refreshToken() function, which throws the exception you're getting.

Your code should probably look something like this:

/* If we have an access token */
if (Cache::has('service_token')) {
    $this->client->setAccessToken(Cache::get('service_token'));

    if ($this->client->isAccessTokenExpired()) {
        $this->client->refreshToken(Cache::get('service_token'));
    }
} else {
    /* whatever code goes here to get the access token for the first time */
}
patricus
  • 59,488
  • 15
  • 143
  • 145
  • Why does the functionality differ from environments? This is the only difference. – sogeniusio Aug 14 '18 at 19:34
  • @sogeniusio My guess is because your local environment has that key in the cache already. Run `php artisan cache:clear` in your local environment and I imagine you'll start to get the same error you are in production. – patricus Aug 14 '18 at 19:37
  • I updated the logic to the following /* If we have an access token */ if (Cache::has('service_token')) { $this->client->setAccessToken(Cache::get('service_token')); $this->client->setAccessToken(Cache::get('service_token')); if ($this->client->isAccessTokenExpired()) { $this->client->refreshToken(Cache::get('service_token')); } } else { Cache::forever('service_token', $this->client->getAccessToken()); } – sogeniusio Aug 14 '18 at 19:48
  • Now i see Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information {"exception":"[object] (DomainException(code: 0): Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information at /var/www/phiportal/vendor/google/auth/src/ApplicationDefaultCredentials.php:156) – sogeniusio Aug 14 '18 at 19:48
0

This is resolved. With the help of patricus

Looks like the location I saved the file was in resources directory which seems to be unreadable. Once moved to storage it was readable. I'll work on finding a final resting place for this file.

sogeniusio
  • 111
  • 3
  • 14