"Service account authorization without OAuth", can we get file from Google Cloud Storage via this approach?

Viewed 250

Per this documentation Addendum: Service account authorization without OAuth, it says

If the API you want to call has a service definition published in the Google APIs GitHub repository, you can make authorized API calls using a JWT instead of an access token

I see Google Cloud Storage published on that Github repository but I can not seem to be able to get a file on Google Cloud Storage with the approach in the documentation. According to JWT header and payload example show in step 3 of the documentation, I did the following adjustments

  • Change kid from abcdef1234567890 to my service account private_key_id
  • Change iss and sub to my service account client_email
  • Change aud to https://storage.googleapis.com/
  • Change iat and exp to now and now + 1 hour

I use signed JWT as bearer token trying to get file from http://storage.googleapis.com/<bucket>/path/file but I keep getting this response

<?xml version='1.0' encoding='UTF-8'?><Error><Code>AuthenticationRequired</Code><Message>Authentication required.</Message></Error>

Can we really use this approach with Google Cloud Storage? Am I doing anything wrong?

Note that I can access the file with oauth access_token when following another approach described on the same page documentation page but I prefer not to make a request to get access_token first before making a request to get the file.

1 Answers

I drew the same conclusion as you from reading this documentation, but it doesn't work for me either on Google Cloud Storage.

It does work on some services like Google Cloud Pub/Sub.

I also confirmed that the Google Cloud SDK doesn't use the "Service account authorization without OAuth" method for GCS, but it does for Pub/Sub.

For example in the Node.js SDK, the variable determining whether or not to use this method is useSelfSignedJWT in google-auth-library:

const useSelfSignedJWT =
  (!this.hasUserScopes() && url) ||
  (this.useJWTAccessWithScope && this.hasAnyScopes());

A service like Pub/Sub that supports self-signed JWT sets useJWTAccessWithScope = true, but GCS doesn't set this variable.

That being said I forced the GCS SDK to use self-signed JWT and got it to work that way!

const { Storage } = require('@google-cloud/storage')

const storage = new Storage({
  keyFilename: 'service-account-key.json'
})

storage.authClient.useJWTAccessWithScope = true

// Use client and watch how it doesn't call the OAuth2 endpoint,
// and requests are still successful.

The interesting thing to note is that the self-signed JWT that Google SDK generated is a bit different from what they document for Service account authorization without OAuth:

{
  "iss": "service-account@project.iam.gserviceaccount.com",
  "sub": "service-account@project.iam.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/iam https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/devstorage.full_control",
  "exp": 1234567890,
  "iat": 1234567890
}

Whereas their documentation says to use a aud field and doesn't mention scopes:

{
  "iss": "123456-compute@developer.gserviceaccount.com",
  "sub": "123456-compute@developer.gserviceaccount.com",
  "aud": "https://firestore.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600
}

So you can set a scope field with a valid OAuth scope, e.g. for GCS. This did work for me on GCS, and it might work for other APIs that don't accept self-signed tokens with aud.

Related