Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpAccessTokenRefreshError: invalid_grant ... one hour limit refresh token

I have looked at the other question regarding this topic and it doesn't seem to match my error. I'm getting the error when running Google Sheets APIv4:

raise HttpAccessTokenRefreshError(error_msg, status=resp.status) HttpAccessTokenRefreshError: invalid_grant

Error occurs on the line service.spreadsheets().values().get(spreadsheetId=key, range=ranges).execute()

This error only pops up sometimes. If I don't do anything and just run the code again. It will take me through the authentication flow process again and I get

Authentication successful. Storing credentials to C:\Users\jason\.credentials\sheets.googleapis.com-python-quickstart.json

After which, I can run any code for a while until the same HttpAccessTokenRefreshError: invalid_grant pops up again and I have to reauthenticate again.

How do I prevent this?

I'm using the code found developers.google.com/sheets/api/quickstart/python.

I've tried to use ntp to sync time with the following

import time
import os
try:
    import ntplib
    client = ntplib.NTPClient()
    response = client.request('pool.ntp.org')
    os.system('date ' + time.strftime('%m%d%H%M%Y.%S',time.localtime(response.tx_time)))
except:
    print('Could not sync with time server.')

print('Done.')

but getting:

The system cannot accept the date entered.
Enter the new data: (mm-dd-yy)

After I enter the current date, nothing happens.

I have also looked at this page. https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35#.5utz2vcn6

This problem also arises when I run code that is longer than 1 hour to finish. On the refresh token. It always bomb.

Now I'm thinking, tokens granted only lasts one hour and on refreshes, it always bombs.

I have posted the code for connecting:

class APIv4:
    def __init__(self):
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
                    'version=v4')
    self.service = discovery.build('sheets', 'v4', http=http,
                              discoveryServiceUrl=discoveryUrl)

def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'sheets.googleapis.com-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials
like image 741
jason Avatar asked Oct 29 '25 14:10

jason


1 Answers

As a general idea, there seems to be a problem with the refresh of your access token in between calls.

That either means that some of your credentials are not passed correctly or there is some problem with your local machine's time (although seems less likely than the first option)

I suggest researching the possibilities stated in this issue: https://github.com/google/oauth2client/issues/451:

  1. (less likely) Why don't you try to force a clock update with ntpdate. Install ntp and give it a try, because a user stated that worked for him
  2. Ok. After a loong research I guess I found out the problem. In fact, refresh_token was missing from the user credential, but the issue was tricky.

    The refresh token is given for the FIRST time when the application asks the user for permissions. The refresh token is given ONLY IF the flow's step 1 includes the parameters approval_prompt="force"

    For some reason the user (me) hadn't got refresh_token in user's credentials, so I revoked permissions from the application on My Account -> Security -> Applications, and restarted the OAuth dance again. Now I got refresh_token.

Update for #2 option:

Following this guide and the recommendation stated above, I believe that you must add to this code snippet (taken directly from the guide):

# Create a state token to prevent request forgery.
# Store it in the session for later validation.
state = hashlib.sha256(os.urandom(1024)).hexdigest()
session['state'] = state
# Set the client ID, token state, and application name in the HTML while
# serving it.
response = make_response(
    render_template('index.html',
                    CLIENT_ID=CLIENT_ID,
                    STATE=state,
                    APPLICATION_NAME=APPLICATION_NAME))

the prompt=consent line and then execute the third step of the quoted response above (option 2).

Another option is to use approval_prompt=force but you must choose between the two, because they don't work well together.

Good luck :)

like image 50
John Moutafis Avatar answered Oct 31 '25 05:10

John Moutafis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!