generate_access_token() with subject for Google service account

Solution for generate_access_token() with subject for Google service account
is Given Below:

In python, I’m trying to call the GMail API via a service account with Delegated domain-wide authority, without using SERVICE_ACCOUNT_FILE.

My objective is to avoid creating a secret Key for the service account. Instead, I gave the Service Account Token Creator role to the process owner (me in local dev, App Engine Service Account in prod).

In the code below I successfully get and use an access token for the service account, without any SERVICE_ACCOUNT_FILE.

from google.cloud.iam_credentials_v1 import IAMCredentialsClient
from google.oauth2.credentials import Credentials
import googleapiclient.discovery

tk = IAMCredentialsClient().generate_access_token(
    name=f'projects/-/serviceAccounts/{service_id}',
    scope=['https://www.googleapis.com/auth/gmail.insert'],
    # subject="[email protected]" doesn't work here :'(
)
service = googleapiclient.discovery.build('gmail', 'v1', credentials=Credentials(tk.access_token))
response = service.users().messages().insert(userId='[email protected]', body=body).execute()

Problem is, after granting permissions to the service account in Google Admin of my.domain, I get the following error:

{‘message’: ‘Precondition check failed.’, ‘domain’: ‘global’, ‘reason’: ‘failedPrecondition’}

I suspect that what I am missing is the subject, i.e. the email of an admin at my.domain.

I know I can provide the subject by constructing the credentials differently:

from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file(key_file, scopes=scopes)
delegated_credentials = credentials.with_subject('[email protected]'). # <- I need this
service = googleapiclient.discovery.build('gmail', 'v1', credentials=delegated_credentials)

But this requires the creation of a secret key, which I’d like to avoid.

Is there a way to pass the subject in the first code example above ?

AFAIK this is not possible

Would be happy to be proved wrong on this!

I place this as a disclaimer because you are right, it does not explicitly deny the possibility. Its not generally a requirement for documentation to explicitly express everything you can not do with it, though I do see the potential for confusion here so it might be worth “Sending Feedback” to the documentation page.

What the instructions say

Preparing to make an authorized API call

After you obtain the client email address and private key from the API Console …

source

In no place does it say “this is an optional step”. This is also true in other places like the Directory API instructions on domain-wide delegation, or the Reports API

The Recommendation to not use Keys

The following is from the article you linked:

Use service account keys only if there is no viable alternative

A service account key lets an application authenticate as a service
account, similar to how a user might authenticate with a username and
password.

source

When you attach a service account, for example to an App Engine instance, the instance is like the user and the service account is its user account. It serves as a sort of identity for the instance. More info on that on this other CodeUtility thread.

So the only way for a service account to act as-if it were another account, is with a key. Likewise the only way for a user to act as-if it were a service account is with a key. Therefore, if you are trying to enable domain-wide delegation, which presupposes that you want to have a service account act as-if it were other accounts, having a key is essential, whether the service account is attached to an App Engine instance or not.

Granted, its not mentioned explicitly that you can not achieve this without a key. Though it doesn’t mention that you can either. From the tests that I have run, I have run into similar results as yours, so it would seem that you just can’t. As mentioned in the disclaimer, I would be happy to be proved wrong, however, I suspect that it would be due to a vulnerability, not intended behavior.

You haven’t mentioned why you need domain-wide delegation of authority, so you may want to evaluate if you really need it. Usually all that is needed is the OAuth flow, in which an app acts oh-behalf of a user, not as-if it were the user.

References