Google API PHP offline access, "invalid_grant: Code was already redeemed"

21,990

There are three types of codes or tokens Googles Authentcation server returns.

  1. Authentication code
  2. Access token
  3. Refresh token.

Authentication code

When a user clicks on the Authentcation form and grants your application access. Google returns to you an Authentcation code. You should take this code and exchange it for an Access token and a refresh token. This code is only used once if you try and use it again you will get an error message.

invalid_grant: Code was already redeemed.

Access token

Access tokens are used to access the APIs this token should be sent along with every request you make. Access tokens are short lived they work for an hour and then they stop working

Refresh token

Refresh tokens should be saved on your server some place. Once the access token expires you can use the refresh token to get a new access token.

Your problem is that you are saving the authentication code which is of no use to you. You need to find the refresh token and save that.

Share:
21,990
unfulvio
Author by

unfulvio

Updated on January 09, 2020

Comments

  • unfulvio
    unfulvio over 4 years

    How to authorize a Google Client permanently until the user revokes authorization?

    I am trying to make an app that connects to Google Calendar. It has to run in PHP and therefore I am using the Google API PHP client provided by google.

    The app needs to have offline access so it works when the user is not in the session. The app is intended to let the user to manage and display his calendars publicly on websites and such.

    I have created credentials in Google Console, using service method (with a client ID and client secret). Using the Google API client I also requested authorization from the user. I open a new browser window, the user authorizes, an authorization code is returned by Google. I take this code, store it and use it to authorize the client, which successfully connects to Google Calendar and exchange data.

    Now, I understood this is a token that is going to expire. Unless one uses offline access, which I set. However, after a few minutes or less I will always get an error: Error fetching OAuth2 access token, message: 'invalid_grant: Code was already redeemed.

    This is the code I use to connect the client:

    $client = new \Google_Client();
    $client->setApplicationName( 'My App' );
    $client->setScopes( array( \Google_Service_Calendar::CALENDAR ) );
    $client->setClientId( $this->google_client_id );
    $client->setClientSecret( $this->google_client_secret );
    $client->setRedirectUri( $this->google_client_redirect );
    $client->setAccessType( 'offline' );
    
    if ( $code = $this->google_client_auth ) {
    
        try {
    
            $client->authenticate( $code );
    
        } catch( \Exception $e ) {
                var_dump( $e );
        }    
    
    }
    
    return new \Google_Service_Calendar( $client );
    

    This is a method inside a class.

    The client ID and the client secret are stored in the app settings.

    I'm also storing the code returned by the user in a setting, but I think this is where I"m doing it wrong? I am putting the link to a Google OAuth window in a separate method (which also uses the same client id and secret and sets offline method as well). And to get the authorization is working. I can get to the calendars. It just doesn't last long...

  • unfulvio
    unfulvio over 8 years
    thanks... but why do I need both the access token and the refresh token immediately? The api has two methods getAccessToken and getRefreshToken. But only one setter setAccessToken. When I obtain the Auth code from user I can run getAuth()->authenticate but the API doesn't say if what it returns is an Access or Refresh token. Do I need to do pass the result of getAuth->authenticate to setAccessToken? What is getAccessToken for, then?
  • unfulvio
    unfulvio over 8 years
    also, from the Google PHP API it appears that the access token is a json object, but the refresh token is a string? So do I have to pass the refresh token to getAuth->authenticate() method?
  • unfulvio
    unfulvio over 8 years