"'Credentials' object has no attribute 'access_token'" when using google-auth with gspread

11,030

Solution 1

As per the gspread documentation, the gspread.authorize method only supports credential objects that are created by the oauth2client library. To work with the new google-auth one, gspread should add support for it.

A possible workaroud, if you don't want to use the oauthclient2 that is deprecated, is to use authlib leveraging the session parameter of the gspread.Client class. There is a nice tutorial on how to do this here.

Update April 27 2020

Starting from version 3.4.0 gspread is now supporting google-auth. You can find all the details in the dedicated documentation. Here is an official statement from the author:

Older versions of gspread have used oauth2client. Google has deprecated it in favor of google-auth. If you’re still using oauth2client credentials, the library will convert these to google-auth for you, but you can change your code to use the new credentials to make sure nothing breaks in the future.

Solution 2

A year and a half later in quite the same situation, I found this to work for me:

import gspread
from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession

credentials = service_account.Credentials.from_service_account_file(
    'your_key_file.json')

scoped_credentials = credentials.with_scopes(
        ['https://spreadsheets.google.com/feeds',
         'https://www.googleapis.com/auth/drive']
        )

gc = gspread.Client(auth=scoped_credentials)
gc.session = AuthorizedSession(scoped_credentials)
sheet = gc.open_by_key('key_in_sharelink')
print(sheet.title)

Solution was adapted from this post in gspread github and the google-auth user guide.

Share:
11,030
Kurt Peek
Author by

Kurt Peek

Hi, I'm Kurt Peek, a backend engineer at Apple.

Updated on June 04, 2022

Comments

  • Kurt Peek
    Kurt Peek almost 2 years

    I'd like to use gspread module to edit Google sheets from Python. The setup instructions contain the following example:

    import gspread
    from oauth2client.service_account import ServiceAccountCredentials
    
    scope = ['https://spreadsheets.google.com/feeds',
             'https://www.googleapis.com/auth/drive']
    
    credentials = ServiceAccountCredentials.from_json_keyfile_name('gspread-april-2cd … ba4.json', scope)
    
    gc = gspread.authorize(credentials)
    

    However, according to https://pypi.org/project/oauth2client/ the oauth2client library is deprecated. So I've tried to adapt this as follows, using google-auth:

    import gspread
    from google.oauth2 import service_account
    
    credentials = service_account.Credentials.from_service_account_file(
        'my_client_secrets.json')
    
    scoped_credentials = credentials.with_scopes(
        ['https://www.googleapis.com/auth/spreadsheets'])
    
    gc = gspread.authorize(scoped_credentials)
    

    Unfortunately, I'm running into the following error:

    (lucy-web-CVxkrCFK) bash-3.2$ python nps.py
    Traceback (most recent call last):
      File "nps.py", line 54, in <module>
        gc = gspread.authorize(scoped_credentials)
      File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/gspread/__init__.py", line 38, in authorize
        client.login()
      File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/gspread/client.py", line 46, in login
        if not self.auth.access_token or \
    AttributeError: 'Credentials' object has no attribute 'access_token'
    

    If I drop into the debugger, I indeed see that credentials has a token attribute, but not an access_token one:

    > /Users/kurtpeek/Documents/Dev/lucy2/lucy-web/scripts/nps.py(54)<module>()
         53 import ipdb; ipdb.set_trace()
    ---> 54 gc = gspread.authorize(scoped_credentials)
         55 
    
    ipdb> type(credentials)
    <class 'google.oauth2.service_account.Credentials'>
    ipdb> type(scoped_credentials)
    <class 'google.oauth2.service_account.Credentials'>
    ipdb> dir(credentials)
    ['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_additional_claims', '_from_signer_and_info', '_make_authorization_grant_assertion', '_project_id', '_scopes', '_service_account_email', '_signer', '_subject', '_token_uri', 'apply', 'before_request', 'expired', 'expiry', 'from_service_account_file', 'from_service_account_info', 'has_scopes', 'project_id', 'refresh', 'requires_scopes', 'scopes', 'service_account_email', 'sign_bytes', 'signer', 'signer_email', 'token', 'valid', 'with_claims', 'with_scopes', 'with_subject']
    

    Are the Credentials generated by google-auth not the same objects as the ones generated by oauth2client?