Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't use proxy to do JWT authentication with googleapis

I have a NodeJS API working behind a corporate proxy and I have been trying to do authentication with Google with their NodeJS client:

const google = require('googleapis');

function getServiceAccountInfo() {
  return {
    type: 'service_account',
    project_id: 'XXXXX',
    private_key_id: 'XXXXXX',
    private_key: 'XXXX',
    client_email: 'XXXX',
    client_id: 'XXXX',
    auth_uri: 'XXXX',
    token_uri: 'XXXX',
    auth_provider_x509_cert_url: 'XXXX',
    client_x509_cert_url: 'XXXXX'
  };
}

const SCOPES = 'https://www.googleapis.com/auth/firebase.messaging';

let proxy2 = {
  host: 'proxy.hkcsl.net',
  port: 8080
};

const proxy3 = {
  proxy: 'http://172.25.2.6:8080'
}

const proxy4 = {
  proxy: {
    host: '172.25.2.6',
    port: 8080,
    auth: {
      username: '',
      password: '',
    },
  }
}

process.env.HTTPS_PROXY = 'https://172.25.2.6:8080';
process.env.https_proxy = 'https://172.25.2.6:8080';
process.env.HTTP_PROXY = 'http://172.25.2.6:8080';
process.env.http_proxy = 'http://172.25.2.6:8080';
google.options({
  proxy: proxy4.proxy
});

const key = getServiceAccountInfo();
const jwtClient = new google.auth.JWT(
  key.client_email,
  undefined, // null,
  key.private_key,
  SCOPES,
  undefined, // null
);
jwtClient.authorize(function(err, tokens) {
  if (err) {
    console.error(err);
    return;
  }
  console.log(tokens.access_token);
});

However, no matter how I configure the proxy option, I still either get a timeout error or something like

ERROR Error: write EPROTO 101057795:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:openssl\ssl\s23_clnt.c:827:

Some people managed to fix it by changing the port to 443 but it doesn't apply to my case since that port is not available to me inside the server. And I've already referenced on the discussions and solutions proposed here:

StackOverflow: node-request - Getting error “SSL23_GET_SERVER_HELLO:unknown protocol”

googleapis: There is no facility for passing a proxy to the oauth2client

Axios: Request to HTTPS with HTTP proxy fails

Using jwtClient.authenticate() behind a proxy results in ETIMEDOUT error

It also seems like Vue is also encountering a similar problem yesterday, they changed from using axios to using request instead. Is there a similar workaround for googleapis too?

like image 580
kevguy Avatar asked Oct 17 '22 22:10

kevguy


1 Answers

After referencing on the workaround regarding the proxy issues with axios, which google-api-nodejs-client (i.e. googleapis) is using, and crawling through the source code of google-auth-library-nodejs (jwtclient.ts) and node-gtoken (index.ts), I wrote the following method to request token from Google manually as a temporary workaround:

const axios = require('axios-https-proxy-fix');
const querystring = require('querystring');
const jws = require('jws');

const GOOGLE_TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token';
const GOOGLE_REVOKE_TOKEN_URL =
    'https://accounts.google.com/o/oauth2/revoke?token=';

const SCOPES = 'https://www.googleapis.com/auth/firebase.messaging';

const PROXY = {
  host: '172.25.2.6',
  port: 8080,
  auth: {
    username: '',
    password: '',
  }
};

function getServiceAccountInfo() {
  return {
    type: 'service_account',
    project_id: 'XXXX',
    private_key_id: 'XXXX',
    private_key: 'XXXX',
    client_email: 'XXXX',
    client_id: 'XXXX',
    auth_uri: 'https://accounts.google.com/o/oauth2/auth',
    token_uri: 'https://accounts.google.com/o/oauth2/token',
    auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
    client_x509_cert_url: 'XXXX'
  };
}

function requestToken(client_email, private_key) {
  const iat = Math.floor(new Date().getTime() / 1000);
  const additionalClaims = {};
  const payload = Object.assign(
    {
      iss: client_email,
      scope: SCOPES,
      aud: GOOGLE_TOKEN_URL,
      exp: iat + 3600,
      iat,
      sub: undefined
    },
    additionalClaims);
  const signedJWT =
    jws.sign({header: {alg: 'RS256'}, payload, secret: private_key});
  return axios({
    method: 'post',
    url: GOOGLE_TOKEN_URL,
    data: querystring.stringify({
      grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
      assertion: signedJWT
    }),
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    proxy: PROXY
  })
  .then(r => {
    const body = r.data;
    console.log(body);
    return body.access_token;
  })
  .catch(e => {
    const body = (e.response && e.response.data) ? e.response.data : {};
    let err = e;
    if (body.error) {
      const desc =
          body.error_description ? `: ${body.error_description}` : '';
      err = new Error(`${body.error}${desc}`);
    }
    throw err;
  });
}

const key = getServiceAccountInfo();
requestToken(key.client_email, key.private_key);

Reference: GitHub

like image 172
kevguy Avatar answered Oct 22 '22 22:10

kevguy