Library Name
google-auth-library
Versions
Broken: 10.6.1 through 11.1.0 (latest). Works: 10.5.0 and earlier.
Node.js 22 and 24, Linux.
Description
A JWT client built from a JSON keyFile, with no email option, signs its token request with no iss claim. Google's token endpoint rejects it:
invalid_grant: Invalid grant: account not found
The same key works when email and key are passed explicitly, and it worked with the same keyFile code up to 10.5.0.
Reproduction
The transporter is stubbed and the key is a throwaway, so nothing is sent to Google:
// key.json: {"type":"service_account","client_email":"test-sa@example-project.iam.gserviceaccount.com","private_key":"<any RSA PEM>"}
const {JWT} = require('google-auth-library');
const client = new JWT({
keyFile: './key.json',
scopes: ['https://www.googleapis.com/auth/drive.readonly'],
});
client.transporter = {
request: async opts => {
const assertion = new URLSearchParams(opts.data).get('assertion');
const claims = JSON.parse(Buffer.from(assertion.split('.')[1], 'base64url'));
console.log(require('google-auth-library/package.json').version, 'iss =', claims.iss);
return {data: {access_token: 't', expires_in: 3600}};
},
};
client.getAccessToken();
10.5.0 iss = test-sa@example-project.iam.gserviceaccount.com
10.6.1 iss = undefined
11.1.0 iss = undefined
Cause
When gtoken was brought into this package (src/gtoken), the keyFile handling changed:
GoogleToken's constructor sets iss from email. With only a keyFile, email is still empty at that point.
TokenHandler.processCredentials() reads the key file later and sets tokenOptions.key and tokenOptions.email, but not tokenOptions.iss.
buildPayloadForJwsSign() signs tokenOptions.iss, so the claim is undefined.
The standalone gtoken package did this.iss = creds.clientEmail || this.iss after reading the key file.
The existing tests don't catch it because every keyFile test in test/test.jwt.ts also passes email.
Expected behavior
The client_email from a JSON key file is used as iss, as before 10.6.1.
Workaround
Read the key file yourself and pass new JWT({email: key.client_email, key: key.private_key, scopes}).
I'll open a PR with a fix and tests.
Library Name
google-auth-library
Versions
Broken: 10.6.1 through 11.1.0 (latest). Works: 10.5.0 and earlier.
Node.js 22 and 24, Linux.
Description
A
JWTclient built from a JSONkeyFile, with noemailoption, signs its token request with noissclaim. Google's token endpoint rejects it:The same key works when
emailandkeyare passed explicitly, and it worked with the samekeyFilecode up to 10.5.0.Reproduction
The transporter is stubbed and the key is a throwaway, so nothing is sent to Google:
Cause
When gtoken was brought into this package (
src/gtoken), thekeyFilehandling changed:GoogleToken's constructor setsissfromemail. With only akeyFile,emailis still empty at that point.TokenHandler.processCredentials()reads the key file later and setstokenOptions.keyandtokenOptions.email, but nottokenOptions.iss.buildPayloadForJwsSign()signstokenOptions.iss, so the claim isundefined.The standalone gtoken package did
this.iss = creds.clientEmail || this.issafter reading the key file.The existing tests don't catch it because every
keyFiletest intest/test.jwt.tsalso passesemail.Expected behavior
The
client_emailfrom a JSON key file is used asiss, as before 10.6.1.Workaround
Read the key file yourself and pass
new JWT({email: key.client_email, key: key.private_key, scopes}).I'll open a PR with a fix and tests.