Search code examples
python-3.xgoogle-cloud-functionsgoogle-cloud-storage

Pass json keyfile from cloud storage to ServiceAccountCredentials function


I have the following few lines of code

credentials = ServiceAccountCredentials.from_json_keyfile_name(
    filename,
    scopes=['https://www.googleapis.com/auth/analytics',
        'https://www.googleapis.com/auth/analytics.edit'])

A filename is expected. My json key is stored in Google Cloud storage. How do I return the filename so I can pass it to the ServiceAccountCredentials assuming my bucket is named bucket_name and my key is name json.key

So far I'm stuck with this:

bucket = read_storage_client.get_bucket(bucket_name)
blob = bucket.get_blob("key.json")
json_data_string = blob.download_as_string()

I can print the json_data_string:

{\n "type": "service_account",\n "project_id": "xxxxxxxxx",\n "private_key_id": "xxxxxxxxxxx",\n "private_key": "-----BEGIN PRIVATE KEY-----\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxQ=\\n-----END PRIVATE KEY-----\\n",\n "client_email": "[email protected]",\n "client_id": "xxxxxxxxxxxxxxxx",\n "auth_uri": "https://accounts.google.com/o/oauth2/auth",\n "token_uri": "https://oauth2.googleapis.com/token",\n "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",\n "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/xxxxxxxxxx"\n}

How should I use this json_data_string variable? how can I pass it the ServiceAccountCredentials function. I'm not sure what is really this json_data_string variable.


Solution

  • You are stuck because you need a credential to have the capability to download your JSON key. "You need a json key for downloading the json key". Quite absurd, no?

    The other solution is to make your bucket public: A secret key in a publicly accessible bucket. Also absurd...

    This is because, it's not the right way. You can deploy a function with a service account, it's named function identity. This solution will make your code far more simpler.

    First, you don't need to get the service account key file from storage, because it's automatically loaded with your function.

    Second, because the service account is automatically loaded into the function, you can simply use the default credential in your code

    credentials = ServiceAccountCredentials.create_scoped(['https://www.googleapis.com/auth/analytics','https://www.googleapis.com/auth/analytics.edit'])
    

    Let me know if you need more.

    EDIT

    I let the first part because I strongly recommend you to not use JSON key file in GCP platform environment. It's horrible to manage: you have to store it securely, you have to rotate it regularly (recommended every 90 days),... If you really want to use it, try secret manager to store and retrieve it safely

    So, to unlock you (because it's the heart of your question), you can have a look to the documentation of the python class. You can see a method named from_json(). But this method doesn't allow scope definition. If you want to use scope, you can use this one from_json_keyfile_dict(). Thereby, I will do something like this

    bucket = read_storage_client.get_bucket(bucket_name)
    blob = bucket.get_blob("key.json")
    json_data_string = blob.download_as_string()
    
    import json
    credentials = ServiceAccountCredentials.from_json_keyfile_dict(
        json.loads(json_data_string),
        scopes=['https://www.googleapis.com/auth/analytics',
            'https://www.googleapis.com/auth/analytics.edit'])
    

    Note: you can download your JSON file stored in Cloud Storage thanks to the default service account used by Cloud Function. It's the compute engine default service account that I don't recommend you to use for 2 reasons: First it's widely open (it is Project Editor), Second, all the services use this default service account if nothing special is defined, execpt some rare services (Scheduler or pubsub for example)