Search code examples
pythonauthenticationgoogle-cloud-platformgmail-apigoogle-cloud-run

How can I authenticate my Cloud Run service to access a GSuite user's gmail messages?


I've been trying to develop a Cloud Run service that has access to a GSuite account's email messages in order to process its attachments, however, I'm having problems to authenticate my Cloud Run service to access Gmail's API. I have the following code deployed in the service:

from googleapiclient.discovery import build
from googleapiclient._auth import default_credentials

SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = default_credentials(scopes=SCOPES)
gmail = build('gmail', 'v1', credentials=creds)

request_body = {
    "topicName": "NAME_OF_MY_PUBSUB_TOPIC"
}
gmail.users().watch(userId="[email protected]", body=request_body).execute()

where [email protected] is the GSuite administrator account and the same account I want to read emails from.

When I deploy the service I get a 400 error saying "Precondition check failed" when trying to make the watch call.

I've read here that when I don't specify a service account for the service, it uses the Application Default Credentials, which defaults to [email protected] for Cloud Run and it full access to the project it is contained out of the box.

I've also enabled Domain-Wide Delegation for [email protected] and added https://www.googleapis.com/auth/gmail.readonly to Google Admin > Security > API Controls > Domain-wide delegation with the same client ID as the default service account. Finally, for the OAuth Consent Screen, I've marked it as internal, added my Cloud Run service URL to Authorized Domains and added https://www.googleapis.com/auth/gmail.readonly to the scopes section.


Solution

  • I've found a solution:

    First, it seems that the default service-account [email protected] can't have Domain-wide delegation. I noticed that when I enabled it, clicked save and refreshed the page, the setting was disabled. I decided to create a new service account, enable Domain-Wide delegation and the setting was correctly saved.

    Second, I used the next script (source) to create the credentials:

    from google.oauth2 import service_account
    from googleapiclient.discovery import build
    
    SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
    creds = service_account.Credentials.from_service_account_file(PATH_TO_FILE, scopes=SCOPES)
    delegated_creds = creds.with_subject('[email protected]')
    gmail = build('gmail', 'v1', credentials=delegated_creds)
    

    so when I deploy the container, I also upload de the credential files. I believe there is a more secure option (with Google Secret Manager for example), but this works for now. Also, I don't know if uploading secrets during deployment is an insecure practice.

    Finally, I don't yet understand how I could use ADC (Application Default Credentials) to avoid uploading the credentials at all.