Search code examples
laravellaravel-5google-apigoogle-api-php-clientgoogle-oauth

How does google-api work in offline request with tokens


My first problem is that when I make instance of the Google_Client and set all the needed scopes, configs, access types and others and then generate a URL for the user to AUTH with $client->createAuthUrl(); I allow access to my application with the google account that I'm logged. Everything works fine until I need to refresh the token.

I explain below about the token refresh.

  $this->client = new Google_Client();
            $this->client->setAuthConfig(Storage::path('secret.json'));
            $this->client->setAccessType("offline");        // offline access
            $this->client->setIncludeGrantedScopes(true);   // incremental auth
            $this->client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
            $this->client->setRedirectUri(env('APP_URL') . '/login/google/callback');

So the next thing I did is to get the google user data with laravel-socialite. This is the data I get:

The code for the request

  public function redirectToGoogleProvider()
    {
        $parameters = ['access_type' => 'offline'];

        return Socialite::driver('google')
            ->scopes(["https://www.googleapis.com/auth/drive"])
            ->with($parameters)
            ->redirect();
    }
User {#466 ▼
  +token: "ya29.GmPhBiSuYJ_oKsvDTUXrj3lJR5jlEYV4x8d0ZuYZLnDJgYL-uePT_KuUkIb-OQUiWBElhm6ljfac5QhDTXYK3qjWjje1vsnZsTAJ7pXyjNAVd2buZy0a6xZCTdnLdFNMKMDMXa6bGbA"
  +refreshToken: null
  +expiresIn: 3599
  +id: "101509757451285102354"
  +nickname: null
  +name: "My name"
  +email: "My email"
  +avatar: "https://lh3.googleusercontent.com/-Qsbr3VmcZ50/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rdS2HIl3LCv5EYCmvyXulG8n-Ap7w/mo/photo.jpg"
  +user: array:12 [▶]
  +"avatar_original": "https://lh3.googleusercontent.com/-Qsbr3VmcZ50/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rdS2HIl3LCv5EYCmvyXulG8n-Ap7w/mo/photo.jpg"
}

There is no refresh token here. I assume that this is because of the way that google works in offline mode

After a user grants offline access to the requested scopes, you can continue to use the API client to access Google APIs on the user's behalf when the user is offline. The client object will refresh the access token as needed.

So I set the token that I get from the request and store in the database to the Google_Client instance with

$client->setAccessToken(Auth::user()->getUserInfo()->refresh_token)

This works. But when the token needs to be refreshed It's not getting refreshed. Instead I'm getting

"Invalid creditianals error".

Google says that:

"The client object will refresh the access token as needed." But this is not happening.

I don't understand If I need to call a special method or how does the Google_Client handle the token refresh.

I've tried using the CODE parameter to fetch the token and refresh token with it.

$client->fetchAccessTokenWithAuthCode($code);

And got this error:

array:2 [▼
  "error" => "invalid_request"
  "error_description" => "Could not determine client ID from request."
]

I'm probably not doing something right but I have no clue what. I'm confused to be honest. Already lost a day on this without any success.


Solution

  • You will only get a refresh token back the first time you request access of the user. After that Google assumes that you saved the refresh token.

    Have the user go to their google account and revoke the access granted to this client application.

    Go to the page showing Apps with access to your account:

    Then have them authenticate your application again. You should see the refresh toke at that time. Make sure you save it.

    revoke

    you may also be able to do

    $client->revokeToken();
    

    which should revoke the access the user granted. However i have not tried this to see if it gets me a new refresh token.

    refresh access

    This is my code for refreshing the access token NOTE its PHP i am not a larvel dev it looks very close you should be able to covert this if it doesnt work out of the box

    /**
     * Authenticating to Google using Oauth2
     * Documentation:  https://developers.google.com/identity/protocols/OAuth2
     * Returns a Google client with refresh token and access tokens set. 
     *  If not authencated then we will redirect to request authencation.
     * @return A google client object.
     */
    function getOauth2Client() {
        try {
    
            $client = buildClient();
    
            // Set the refresh token on the client. 
            if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
                $client->refreshToken($_SESSION['refresh_token']);
            }
    
            // If the user has already authorized this app then get an access token
            // else redirect to ask the user to authorize access to Google Analytics.
            if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
    
                // Set the access token on the client.
                $client->setAccessToken($_SESSION['access_token']);                 
    
                // Refresh the access token if it's expired.
                if ($client->isAccessTokenExpired()) {              
                    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
                    $client->setAccessToken($client->getAccessToken()); 
                    $_SESSION['access_token'] = $client->getAccessToken();              
                }           
                return $client; 
            } else {
                // We do not have access request access.
                header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
            }
        } catch (Exception $e) {
            print "An error occurred: " . $e->getMessage();
        }
    }