google-auth-library-nodejs
google-auth-library-nodejs copied to clipboard
How to get default service account client email property programmatically ?
Hi, I have some functionality that currently uses this very old NPM library https://www.npmjs.com/package/google-auto-auth to programmatically retrieve the client email property assigned to the default service account directly, without having to download the credentials JSON file.
It works fine but this google-auto-auth library is very old and has multiple security vulnerabilities reported, not to mention that is no longer maintained. I would like to migrate this functionality to use the official google-auth-library library, but I couldn't find in documentation or examples anything close to my use case.
const rp = require('request-promise-native');
const googleAuth = require('google-auto-auth');
const auth = googleAuth();
function generateTokenForCurrentServiceAccount(ttlObj, host) {
return new Promise((resolve, reject) => {
// A series of calls into google-auto-auth follow. As of Feb '18 this library does not appear
// to easily support promises, even using util.promisify(), so callbacks are used below.
// Get the credentials object for the current service account, so we can access the email address
auth.getCredentials((credErr, credentials) => {
if (credErr) {
logger.error(`Error in getCredentials step: ${credErr}`);
reject(credErr);
} else {
auth.getProjectId((prjErr, projectId) => {
if (prjErr) {
logger.error(`Error in getProjectId step: ${prjErr}`);
reject(prjErr);
} else {
auth.authorizeRequest({
method: 'post',
uri: `https://iam.googleapis.com/v1/projects/${projectId}/serviceAccounts/${credentials.client_email}:signJwt`,
}, (authErr, authorizedReqOpts) => {
if (authErr) {
logger.error(`Error in authorizeRequest step: ${authErr}`);
reject(authErr);
} else {
const currentTime = moment();
const currentTimeSeconds = currentTime.unix();
const expirationTimeSeconds = currentTimeSeconds + ttlObj.ttlInSeconds;
// Form and send web service request to Google to have a JWT claim set signed by the service account.
// This request has the OAuth token in the header.
const options = {
method: 'POST',
uri: authorizedReqOpts.uri,
headers: {
Authorization: authorizedReqOpts.headers.Authorization,
'content-type': 'application/json',
},
body: {
payload: JSON.stringify({
iat: currentTimeSeconds,
exp: expirationTimeSeconds,
aud: host,
iss: credentials.client_email,
sub: credentials.client_email,
}),
},
json: true,
};
logger.debug(`Submitting this request to Google: ${JSON.stringify(options.body)}`);
rp(options).then(parsedBody => {
// Successfully signed token, returning it to client
const token = parsedBody.signedJwt;
resolve({
token,
tokenExpiry: expirationTimeSeconds,
});
}).catch(err => {
logger.error(`Error calling GCP to sign JWT: ${err}`);
reject(err);
});
}
});
}
});
}
});
});
}
can you please point me in the right direction ?
any thoughts about this question ?
@rvillane I don't believe we currently expose this property (or at least it would not appear to be public on the class).
I'm not sure if there's a good reason as to why it's private; perhaps you could experiment with running a copy of this library from GitHub, and see if the private field is populated with the value you expect?
Hi @rvillane, you can get the client email with getClient
(using a service account to authenticate), like so (note that this may not work for TS):
const compute = google.compute('v1');
async function main () {
const auth = new google.auth.GoogleAuth({
// Scopes can be specified either as an array or as a single, space-delimited string.
scopes: ['https://www.googleapis.com/auth/compute'],
});
const authClient = await auth.getClient();
console.log(authClient.email);
}
main().catch(console.error);
However, I think it would be better to use the auth flow listed in the README, you shouldn't need to get the email to authenticate.