Description
REPRO CASE
- Create a Service account in GCP (if you don't have one)
- Create a user account in GCP (you probably have one)
- Allow the user account to run as (impersonate) the service account
- Run
gcloud auth application-default login --impersonate-service-account=SERVICE-ACCOUNT-NAME
- Login as your user account
- Start R and run
gargle::credentials_app_default()
(or something that calls it, likegargle::token_fetch
orbigrquery::bq_auth()
)
Expected behavior: Successfully authenticates and acquires a token that authenticates as the service account, OR, fails in some explicit way (at least when debugging is enabled via options(gargle_quiet = FALSE)
)
Actual behavior: Prints that it found an ADC file, but then returns NULL
instead of a token:
> print(gargle::credentials_app_default())
trying `credentials_app_default()`
file exists at ADC path:
/root/credentials.json
NULL
EXPLANATION
Impersonation credentials are a relatively new and relatively obscure but very useful and increasingly supported type of credentials for GCP, see:
- the docs on service account impersonation
- the docs for
--impersonate-service-account
andgcloud auth application-default login
- the
google.auth.impersonated_credentials
module in the Python google auth library - a blog post about doing this in Node
- a thread about adding this feature to the golang oauth2 library
Here is what these "impersonation credentials" look like:
{
"delegates": [],
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<SERVICE ACCOUNT NAME>",
"source_credentials": {
"client_id": "<USER CLIENT ID>",
"client_secret": "<USER CLIENT SECRET>",
"refresh_token": "<USER REFRESH TOKEN>",
"type": "authorized_user"
},
"type": "impersonated_service_account"
}
As you can see, they include a nested set of user credentials, plus instructions for how to use an API to get a token that impersonates the service account (without directly having the service account's credentials). The idea is that when fetching a token for these credentials, first you fetch a token for the nested user credentials, then you use that token to fetch a token to impersonate the service account, then you use that token.
At the very least, credentials_app_default.R
should issue some sort of message if it falls of the end of the recognized info$type
conditions, so that we'd see something like unknown ADC cred type: "impersonated_service_account"
and have a clue about what's going wrong. Better yet, of course, would be to in fact support this type of credential properly...!