Skip to content

google-auth-library: JWT with a JSON keyFile signs without iss since 10.6.1 (invalid_grant: account not found) #9469

Description

@Marinski

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions