Google Cloud Run oAuth with Node

If you're invoking a google cloud run service from your own server, you'll have to manually generate the proper JWT token in order to acces to it. It can be complicated quickly via the twists and turns of google documentation ... Here is my personal reminder :).

Google Cloud Run oAuth with Node

If you're invoking a google cloud run service from your server, you'll have to manually generate the proper JWT token to access the service. It can be quickly complicated ... the twists and turns of google documentation ... Here are my reminders :).

Create the service account

gcloud beta iam service-accounts create service_name --description "service_description" --display-name "service_name_displayed" --project
"gcloud_project_id"

nb: you will probably need to istall this command group
nb: it's project and not project-id like in google official doc O.O

Add the service account

Add the service account to the access list of your services. Normally you can do this on the command line but for me, it only worked through the interface ... (GCloud beta run services add-iam-policy-binding ..)

  1. Go to your IAM interface in service accounts section
  2. Select your project name
  3. You will see your user in the list, get his JSON key, and copy his mail
  4. Go to your google cloud run page and select your services
  5. Show info panel, add member, past the mail and add role "Cloud Run Invoker"

Use this service account from node

As explained in the doc you have three steps :

  1. Self-sign a service account JWT with the target_audience claim set to the URL of the receiving service.
  2. Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the above URL.
  3. Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the service.

you have two solutions for this, code everything by yourself, or use a library.

With google-auth-library

const path = require('path');
const { JWT } = require('google-auth-library');
const keys = require(path.resolve('./config/serviceaccountkey.json'));

async function exec() {

  const serviceUrl = 'https://yoururl.run.app';
  const client = new JWT({
    email: keys.client_email,
    key: keys.private_key,
    additionalClaims: { target_audience: serviceUrl },
  });
  
  const res = await client.request({
    url: serviceUrl,
    method: 'POST',
    data: {
     ....
    },
  });
}

exec().catch(console.error);

By yourself

googleAuth.js

const path = require('path');
const axios = require('axios');
const querystring = require('querystring');
const jws = require('jws');

const keys = require(path.resolve('./config/serviceaccountkey.json'));

const createSignedJwt = (serviceUrl) => {
  const iat = Math.floor(new Date().getTime() / 1000);
  const exp = iat + 3600; // 3600 seconds = 1 hour
  const payload = {
    iss: keys.client_email,
    sub: keys.client_email,
    target_audience: serviceUrl,
    aud: 'https://www.googleapis.com/oauth2/v4/token',
    exp,
    iat,
  };
  const signature = jws.sign({
    header: { alg: 'RS256', typ: 'JWT', kid: keys.private_key_id },
    payload,
    privateKey: keys.private_key,
  });
  return signature;
};

exports.exchangeJwtForToken = async (serviceUrl) => {
  const data = {
    grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    assertion: createSignedJwt(serviceUrl),
  };

  const header = {
    'Content-Type': 'application/x-www-form-urlencoded',
  };
  const res = await axios.post('https://www.googleapis.com/oauth2/v4/token', querystring.stringify(data), header);
  return res.data.id_token;
};

yourfile.js

const path = require('path');
const axios = require('axios');
const googleAuth = require(path.resolve('./googleAuth.js));

exports.exec = async () => {
  try {
    const token = await googleAuth.exchangeJwtForToken('https://yoururl.run.app');

    const res = await axios({
      method: 'post',
      url: 'https://yoururl.run.app',
      headers: { Authorization: `Bearer ${token}` },
      data: {},
    });
    console.log(res.data);
  } catch (e) {
    console.log('error', e);
  }
};


I haven't found information about this, hope I helped some of you :)