Question

I am trying to build a web app that accesses Google Analytics API, and pull data. However, I have having some issues with the OAuth 2.0 authorization.

It allows for successful initial access, but it quickly kicks me out and throws a Google_Auth_Exception with message 'Error fetching OAuth2 access token, message: 'invalid_grant'' when I hit a submit button that refreshes the page.

As I understand OAuth 2.0, there are 4 steps to authentication:

  1. Obtain OAuth 2.0 credentials from Google Dev Console
  2. Obtain an access token from Google Authorization Server
  3. Send the access token to Google Analytics API
  4. Refresh the access token, if necessary

And as I understand it, $client->setAccessToken(); automatically refreshes the token.

I cannot seem to find any documentation from Google since they moved to Github, and I have followed their example structures for the most part.

The error is thrown from the first try block, when it tries to execute $client->authenticate($_GET['code']);

My current workaround is to unset the session token, and have the user re-authorize. However, this is really cumbersome and intrusive, as any interaction with the page will ask for re-authorization.

Any help would be greatly appreciated!

Here is my code:

<?php

    /**********************
    OAUTH 2.0 AUTHORIZATION
    ***********************/

    //required libraries
    set_include_path("../src/" . PATH_SEPARATOR . get_include_path());
    require_once 'Google/Client.php';
    require_once 'Google/Service/Analytics.php';


    //variables
    $client_id = 'redacted';
    $client_secret = 'redacted';
    $redirect_uri = 'http://'.$_SERVER["HTTP_HOST"].$_SERVER['PHP_SELF'];
    $dev_key = 'redacted';

    //create a Google client
    $client = new Google_Client();
    $client->setApplicationName('App');

    //sets client's API information
    $client->setClientId($client_id);
    $client->setClientSecret($client_secret);
    $client->setRedirectUri($redirect_uri);
    $client->setDeveloperKey($dev_key);
    $client->setScopes(array('https://www.googleapis.com/auth/analytics.readonly'));

    //if log out is requested, revoke the access
    if (isset($_REQUEST['logout'])) {
        unset($_SESSION['token']);
    }

    //check if authorization code is in the URL
    try{
        if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']); //does authorization work
    $_SESSION['access_token'] = $client->getAccessToken(); //gets valid access token
    $redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']; //set into session storage
    header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL)); //cleans up the URL
}
}
//if the authorization code is now invalid
catch (Google_Auth_Exception $e) {
    unset($_SESSION['token']); //unset the session token
    echo "Token now invalid, please revalidate. <br>";
}

//if there is an access token in the session storage
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
    $client->setAccessToken($_SESSION['access_token']); //set the client's access token

    //try creating an analytics object
    try {
        $analytics = new Google_Service_Analytics($client);
        echo 'Created Google Analytics Client successfully! <br><br>';
    }
    catch (Google_Auth_Exception $e) {
        echo 'Need authorization!';
    }
} else {
    $authUrl = $client->createAuthUrl(); //create one
    echo "<a class='login' href='$authUrl'><button>Authorize Google Access</button></a>"; //print button
}
Was it helpful?

Solution

I resolved the issue. I was trying to authorize the same authentication code twice, and therefore it returned an invalid_grant error.

My solution was to rewrite much of the code and fix the OAuth2 logic.

I have created a mini-tutorial of the OAuth2 authentication flow below:

<?php
    session_start(); // Create a session

    /**************************
    * Google Client Configuration
    *
    * You may want to consider a modular approach,
    * and do the following in a separate PHP file.
    ***************************/

    /* Required Google libraries */
    require_once 'Google/Client.php';
    require_once 'Google/Service/Analytics.php';

    /* API client information */
    $clientId = 'YOUR-CLIENT-ID-HERE';
    $clientSecret = 'YOUR-CLIENT-SECRET-HERE';
    $redirectUri = 'http://www.example.com/';
    $devKey = 'YOUR-DEVELOPER-KEY-HERE';

    // Create a Google Client.
    $client = new Google_Client();
    $client->setApplicationName('App'); // Set your app name here

    /* Configure the Google Client with your API information */

    // Set Client ID and Secret.
    $client->setClientId($clientId);
    $client->setClientSecret($clientSecret);

    // Set Redirect URL here - this should match the one you supplied.
    $client->setRedirectUri($redirectUri);

    // Set Developer Key and your Application Scopes.
    $client->setDeveloperKey($devKey);
    $client->setScopes(
        array('https://www.googleapis.com/auth/analytics.readonly')
    );

    /**************************
    * OAuth2 Authentication Flow
    *
    * You may want to consider a modular approach,
    * and do the following in a separate PHP file.
    ***************************/

    // Create a Google Analytics Service using the configured Google Client.
    $analytics = new Google_Service_Analytics($client);

    // Check if there is a logout request in the URL.
    if (isset($_REQUEST['logout'])) {
        // Clear the access token from the session storage.
        unset($_SESSION['access_token']);
    }

    // Check if there is an authentication code in the URL.
    // The authentication code is appended to the URL after
    // the user is successfully redirected from authentication.
    if (isset($_GET['code'])) {
        // Exchange the authentication code with the Google Client.
        $client->authenticate($_GET['code']); 

        // Retrieve the access token from the Google Client.
        // In this example, we are storing the access token in
        // the session storage - you may want to use a database instead.
        $_SESSION['access_token'] = $client->getAccessToken(); 

        // Once the access token is retrieved, you no longer need the
        // authorization code in the URL. Redirect the user to a clean URL.
        header('Location: '.filter_var($redirectUri, FILTER_SANITIZE_URL));
    }

    // If an access token exists in the session storage, you may use it
    // to authenticate the Google Client for authorized usage.
    if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
        $client->setAccessToken($_SESSION['access_token']);
    }

    // If the Google Client does not have an authenticated access token,
    // have the user go through the OAuth2 authentication flow.
    if (!$client->getAccessToken()) {
        // Get the OAuth2 authentication URL.
        $authUrl = $client->createAuthUrl();

        /* Have the user access the URL and authenticate here */

        // Display the authentication URL here.
    }

    /**************************
    * OAuth2 Authentication Complete
    *
    * Insert your API calls here 
    ***************************/

OTHER TIPS

in my case it was problem of reauthentication. I was testing the api and got the code. To get the access token i had to revoke the access from account section->security->apps connected. Now select you app name and remove it. now try it and you will get the token response.

Error was : Uncaught exception 'Google_Auth_Exception' with message 'Error fetching OAuth2 access token, message: 'invalid_grant: Code was already redeemed

After added

header('Location: '.filter_var($redirectUri, FILTER_SANITIZE_URL));

I got error message Invalid Request Parameter. How to solve it?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top