21 January 2021

Google Cloud Service Account Authentication

Whilst there is a lot of documentation available on the Google Cloud sites, it is hopelessly disorganised, the examples contradict each other, and in general it is very confusing. So, after spending hours trying to understand what was required to authenticate a service account and get an oauth2 access token, I thought I’d write up my notes into a technology-agnostic recipe to save you some pain.

To be able to use the APIs at all, you’ll first need to go into the Google Cloud console, enable the APIs you wish to use [1], and create a Service Account, assign it the permissions you need, and then create and download a key file for it [2].

If you open the keyfile, you’ll see that it is a standard JSON object, with a collection of values, but the only ones you’ll need are client_email and private_key.

The request itself is a standard HTTPS request which contains a JSON Web Token (JWT) [3], that needs to be constructed using the following approach: 

 

JWT Header

{
    "typ": "JWT",
    "alg": "RS256"
}


JWT Payload

{
    "iss": "billy@domain.org",
    "aud": "https://oauth2.googleapis.com/token",
    
"iat": 1611220000,
    
"exp": 1611220030,
    "scope": "https://www.googleapis.com/auth/cloud-platform"
}


When constructing your own JWT payload, you’ll obviously need to use different values to suit your needs. The iss value will be the client_email value from the key file, the iat will be the current time as a unix epoch seconds value, and exp will be the iat plus a suitable duration to allow you to use the JWT (30 seconds is more than enough), and the scope will be a space-delimited list of Google Cloud scopes [4].

Sign the JWT using the private_key from the key file, using RSA, SHA256 and PKCS1 padding.

Finally, make an HTTPS POST request with the JWT in the assertion parameter of the body, and the oauth2 access_token should be returned in the response:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
User-Agent: Mozilla/5.0
Accept-Encoding: br,gzip
Accept-Language: en-GB,en;q=0.5
Accept: */*
Connection: keep-alive
Content-Length: 822
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3aietf%3aparams%3aoauth%3agrant-type%3ajwt-bearer&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJiaWxseUBkb21haW4ub3JnIiwiYXVkIjoiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLCJpYXQiOjE2MTEyMjIxMDIsImV4cCI6MTYxMTIyMjEzMiwic2NvcGUiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2Nsb3VkLXBsYXRmb3JtLnJlYWQtb25seSBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2NvbXB1dGUucmVhZG9ubHkgaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9uZGV2LmNsb3VkZG5zLnJlYWRvbmx5In0.A-eWGCpR5qDvHyxSDtqAcUMrPRSYhVAlXmfJol0kFMAyMbqdDBFMMofevnhjDBLNqXu4YJchFLG5Yb3BgAW78bMX7VDZVeHvn0TBI4qb8-_rfe2YEWZCKegXHF_56q5_i3iGjVgEKVMwFWK6hTGToIjnb-u3ir0mPbS5y5BhufD-054YhQXLqHEIMpRIRg10SqKVor7CLDJCbkRCbfH7auSXIhRV8_ybHwsck1bE_BFbThZ5dSLpsi2Y28vYiJp_JzY2oyHTGc2P98JKhR-CXvJba_o1aapm8XS77CH3V4Nlu01HY5THwl-UVx_c8KQUPf6eNEscyHec-mt_C6ypjw


  1. https://cloud.google.com/apis/docs/getting-started#enabling_apis
  2. https://cloud.google.com/iam/docs/creating-managing-service-accounts
  3. https://jwt.io/introduction
  4. https://developers.google.com/identity/protocols/oauth2/scopes