Getting error using Google cloud client libraries for Go: unknown credential type: "impersonated_service_account"?

I am working with Google Cloud in Go and following this article by John Hanley:

https://www.jhanley.com/google-cloud-improving-security-with-impersonation/

and mashed it with this SO answer:

How to authenticate Google APIs (Google Drive API) from Google Compute Engine and locally without downloading Service Account credentials?

The credentials are successfully saved to, "application_default_credentials.json":

Notice: "type": "impersonated_service_account"

    {
  "delegates": [],
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/[[email protected]]:generateAccessToken",
  "source_credentials": {
    "client_id": "...apps.googleusercontent.com",
    "client_secret": "...",
    "refresh_token": "...",
    "type": "authorized_user"
  },
  "type": "impersonated_service_account"
}

My code which produces an unknown credential type: "impersonated_service_account" error:

package main

import (
...
    "cloud.google.com/go/storage"
    "golang.org/x/oauth2"
    "google.golang.org/api/docs/v1"
    "google.golang.org/api/drive/v3"
    "google.golang.org/api/impersonate"
    "google.golang.org/api/option"
...
)

var Config.GoogleServiceAccount string = "[email protected]"




func main(){
  _ = getTokenAsImpersonator()
}

// From: https://pkg.go.dev/google.golang.org/api/impersonate#example-CredentialsTokenSource-ServiceAccount
func getTokenAsImpersonator() oauth2.TokenSource {
    ctx := context.Background()

    // Base credentials sourced from ADC or provided client options.
    ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
        TargetPrincipal: Config.GoogleServiceAccount,
        Scopes:          []string{"https://www.googleapis.com/auth/cloud-platform"},
        // Delegates: []string{"[email protected]"},
    })
    if err != nil {
        log.Fatal(err)
    }

    return ts
}

The 'unknown credential type: "impersonated_service_account"' error:

google: error getting credentials using GOOGLE_APPLICATION_CREDENTIALS environment variable: unknown credential type: "impersonated_service_account"

Have I done something wrong or is this a bug?


UPDATE

Answering John's questions from the comments:

1.

a) What is the value of the environment variable GOOGLE_APPLICATION_CREDENTIALS?

GOOGLE_APPLICATION_CREDENTIALS=/Users/x/.config/gcloud/application_default_credentials.json

b) What command did you use to generate application_default_credentials.json?

gcloud auth application-default login --scopes=https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/accounts.reauth,openid --impersonate-service-account=[[email protected]]


Response:

Credentials saved to file: [/Users/x/.config/gcloud/application_default_credentials.json]


c)Which OS and version?

MacOS 10.13.6

d)gcloud --version?

Google Cloud SDK 343.0.0
app-engine-go 
app-engine-python 1.9.91
bq 2.0.69
cloud-datastore-emulator 2.1.0
core 2021.05.27
gsutil 4.62
  1. If you can create a minimum example ...

I have updated the example code above.


At some point I had used the CLI to impersonate an account:

gcloud config set auth/impersonate_service_account <service account>

Then later on when trying to use the application default credentials command it wraps your credentials with the service account credentials.

gcloud auth application-default login

What you end up with is a file that looks like this:

{
  "delegates": [],
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/[email protected]:generateAccessToken",
  "source_credentials": {
    "client_id": "123abc.apps.googleusercontent.com",
    "client_secret": "XXXXXXXXX",
    "refresh_token": "XXXXXXXXX",
    "type": "authorized_user"
  },
  "type": "impersonated_service_account"
}

This appears to cause a lot of problems with third party services such as terraform.

What is strange is that Terraform is just making API calls to Google using Google SDKs, so really its something to do with Google.

You need to remove the impersonation:

gcloud config unset auth/impersonate_service_account

And then run the application default credential command again:

gcloud auth application-default login

Now if you check your file it should look like this:

{
  "client_id": "XXXXXXXXX",
  "client_secret": "XXXXXXXXX",
  "quota_project_id": "example-project",
  "refresh_token": "XXXXXXXXXX",
  "type": "authorized_user"
}

I was hitting the same issue when I was trying to impersonate an account so I could run Terraform commands as a service account instead of my personal account but it doesn't like that.

EDIT: Rereading you question it sounds like you're in the same boat as me. We want to use service accounts without physically downloading the keys. This is even mentioned by Google as best practice. But doing so is causing issues with their own SDKs.