Search code examples
scalagoogle-drive-apigoogle-kubernetes-engineservice-accounts

GKE Workload Identity and Google Services Scala


So I've run into an issue trying to use Google Services APIs in scala from GKE. I have the following example code

object GoogleDrive {
    def createSpreadSheet(name: String, folder: String): String = {
        val file = new File()
            .setParents(List(folder).asJava)
            .setMimeType("application/vnd.google-apps.spreadsheet")
            .setName(name)
        s"https://docs.google.com/spreadsheets/d/${service.files().create(file).setFields("id").execute().getId()}/"
    }

    private lazy val service = authorize()
    private def authorize(): Drive = {
        val httpTransoport = GoogleNetHttpTransport.newTrustedTransport()
        val jsonFactory = GsonFactory.getDefaultInstance()
        val credential = GoogleCredential.getApplicationDefault(httpTransoport, jsonFactory).createScoped(List(DriveScopes.DRIVE_FILE).asJava)
        credential.refreshToken()

        new Drive.Builder(httpTransoport, jsonFactory, credential).setApplicationName("whatever-app").build()
    }
}

This runs into ACCESS_TOKEN_SCOPE_INSUFFICIENT issue. When I test the flow through curl execing into the pod it works, so all the permissions are set correctly.

After investigating through tcpdump I noticed that running above code, the original request for token does not set scope. When manually I would do GET /computeMetadata/v1/instance/service-accounts/default/token?scope=https://www.googleapis.com/auth/drive.file scala does just GET /computeMetadata/v1/instance/service-accounts/default/token (which is kind of funny considering that if you don't set scope when creating GoogleCredential it will crash)

I tried this with few https://mvnrepository.com/artifact/com.google.api-client/google-api-client versions. I even tried this with 2.2.0 for which I had to remove Drive code because it doesn't work with this version (or any services api lib for that matter). They all behave the same, no scope is being set on token request.

What am I doing wrong? Or maybe the scopes should be set somewhere on infrastructure level?

NOTE: code above is just short example. In reality there will be more Drive and Sheets operations so just doing requests myself isn't much of an option.


Solution

  • So for legacy OAuth stuff the scopes are supposed to be set per machine instance (https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam). In our case we had a problem setting those on the Workload Identity. In the end we opted to perform token call ourselves with set scopes as the google library allows for passing of token with setFromTokenResponse method in GoogleCredential object.