GSpread pass credentials from Python not JSON
Asked Answered
C

4

5

Im using GSpread trying to pass the content on my JSON file (Google API Service Application credentials) as a python Dictionary on my script. Im trying to not to carry a json file wherever I take my script.

I get the following error when I tried to pass a dictionary instead of a json file on the following line:

credentials = ServiceAccountCredentials.from_json_keyfile_name(auth_gdrive(), scope)

TypeError: expected str, bytes or os.PathLike object, not set

### auth_gdrive() returns a dictionary like this:

def auth_gdrive():
    dic = {
        "type": "miauuuuuu",
        "pass": "miauuuu"
    }

Im not allow to show whats really in the dic.

Consist answered 19/9, 2018 at 20:14 Comment(2)
the json file will be read as text, i don't think it inherently knows to translate the dictionary object to textCahill
I think you should dig in the code of the from_json_keyfile_name method to see what it uses the parameter for/what it does, or look for alternative method, maybe from_json_keyfile_dictDempsey
C
10

Since I wanted to pass the credentials details from within my application , and not from a json file I couldn't use:

ServiceAccountCredentials.from_json_keyfile_name()

from_json_keyfile_name() expects a json file. But looking into the docs I found the following:

ServiceAccountCredentials.from_json_keyfile_dict()

This will expect an dict object , this is all I needed.

Link:

https://oauth2client.readthedocs.io/en/latest/source/oauth2client.service_account.html

Thank you everyone again

Consist answered 21/9, 2018 at 0:48 Comment(0)
H
3

Additional tip: I am using Google API to read Google Drive files, but also using AWS. I stored the service account credentials in AWS Secrets Manager, so that I did not need a file. I copy-pasted each key-value pair from the downloaded JSON file into AWS Secrets Manager. But I kept getting the error:

Traceback (most recent call last):
  File "./copy_from_google_drive_to_s3.py", line 301, in <module>
    sys.exit(main())
  File "./copy_from_google_drive_to_s3.py", line 96, in main
    keyfile_dict=keyDict, scopes=scopes,
  File "/usr/local/lib/python3.7/site-packages/oauth2client/service_account.py", line 253, in from_json_keyfile_dict
    revoke_uri=revoke_uri)
  File "/usr/local/lib/python3.7/site-packages/oauth2client/service_account.py", line 185, in _from_parsed_json_keyfile
    signer = crypt.Signer.from_string(private_key_pkcs8_pem)
  File "/usr/local/lib/python3.7/site-packages/oauth2client/_pure_python_crypt.py", line 182, in from_string
    raise ValueError('No key could be detected.')
ValueError: No key could be detected.

I had to convert the string representation of newline back into newline:

# Last part of using AWS Secrets Manager, returns json string.
sa_creds = get_secret_value_response['SecretString']
# Convert JSON string to dict.
sa_creds = json.loads(sa_creds)
# In the private key, 1-char newline got replaced with 2-char '\n'
sa_creds['private_key'] = sa_creds['private_key'].replace('\\n', '\n')
credentials = ServiceAccountCredentials.from_json_keyfile_dict(
    keyfile_dict=sa_creds,
    scopes=['https://www.googleapis.com/auth/drive.readonly',]
)
Heavyweight answered 15/8, 2020 at 1:10 Comment(1)
I had the same problem, the suggested solution works even without usage of the deprecated oauth2client.Twoway
L
2

My solution is close to Bob McCormick. The difference is that it's using the credentials method for using service account info instead of JSON file.

Here i'm using Googles Secret Manager to import service account information so that my code can connect to a different GCP project:

from google.cloud import secretmanager
from google.oauth2 import service_account

# Create the Secret Manager client.
secret_client = secretmanager.SecretManagerServiceClient()
    
# Build the resource name of the secret version.
name = f"projects/{project-id}/secrets/{very-secret-secret-name}/versions/latest"
    
# Access the secret version.
secret_response = secret_client.access_secret_version(request={"name": name})
    
# Getting the secret data                                                               
secret_payload = json.loads(secret_response.payload.data.decode("UTF-8"))
    
# Applying the credentials as INFO instead of JSON
credentials = service_account.Credentials.from_service_account_info(
                secret_payload,
                scopes=["https://www.googleapis.com/auth/cloud-platform"],
               )

Lietuva answered 1/4, 2021 at 8:28 Comment(0)
H
0

Since you're using ServiceAccountCredentials, I'm assuming you're using OAuth2 for authorization. You can skip the json file by using oauth2client.SignedJwtAssertionCredentials to create the appropriate credentials object and pass that to gspread.authorize.

import gspread
from oauth2client.client import SignedJwtAssertionCredentials

credentials = SignedJwtAssertionCredentials(service_account_name, private_key.encode(), 
                                            ['https://spreadsheets.google.com/feeds'])
gclient = gspread.authorize(credentials)

UPDATE: It appears that oauth2client.SignedJwtAssertionCredentials has been deprecated in favor of oauth2client.service_account.ServiceAccountCredentials, which only supports json and p12 keyfiles.

Headwards answered 19/9, 2018 at 21:7 Comment(1)
Thank you everyone, like you said SignedJwtAssertionCredentials has been deprecated , and now we should be using ServiceAccountCredentials.Consist

© 2022 - 2025 — McMap. All rights reserved.