Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.

Commit b0ddb75

Browse files
authored
fix: Fixing Implementation of GoogleAuth.sign() for external account credentials (#1397)
* fix: Fixing Implementation of GoogleAuth.sign() for external account credentials Currently, creating signed storage URLs does not work for external account credentials because the storage library expects client_email to be returned from GoogleAuth.getCredentials(). Changing the logic so the same client email that is used to the sign the blob (extracted from the Service Account Impersonation URL) is returned from the getCredentials() call. Fixes #1239 * addressing code review comments * addressing code review comments
1 parent 5df4753 commit b0ddb75

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

src/auth/googleauth.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -730,8 +730,10 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
730730
/**
731731
* The callback function handles a credential object that contains the
732732
* client_email and private_key (if exists).
733-
* getCredentials checks for these values from the user JSON at first.
734-
* If it doesn't exist, and the environment is on GCE, it gets the
733+
* getCredentials first checks if the client is using an external account and
734+
* uses the service account email in place of client_email.
735+
* If that doesn't exist, it checks for these values from the user JSON.
736+
* If the user JSON doesn't exist, and the environment is on GCE, it gets the
735737
* client_email from the cloud metadata server.
736738
* @param callback Callback that handles the credential object that contains
737739
* a client_email and optional private key, or the error.
@@ -752,7 +754,14 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
752754
}
753755

754756
private async getCredentialsAsync(): Promise<CredentialBody> {
755-
await this.getClient();
757+
const client = await this.getClient();
758+
759+
if (client instanceof BaseExternalAccountClient) {
760+
const serviceAccountEmail = client.getServiceAccountEmail();
761+
if (serviceAccountEmail) {
762+
return {client_email: serviceAccountEmail};
763+
}
764+
}
756765

757766
if (this.jsonContent) {
758767
const credential: CredentialBody = {
@@ -884,23 +893,6 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
884893
return sign;
885894
}
886895

887-
// signBlob requires a service account email and the underlying
888-
// access token to have iam.serviceAccounts.signBlob permission
889-
// on the specified resource name.
890-
// The "Service Account Token Creator" role should cover this.
891-
// As a result external account credentials can support this
892-
// operation when service account impersonation is enabled.
893-
if (
894-
client instanceof BaseExternalAccountClient &&
895-
client.getServiceAccountEmail()
896-
) {
897-
return this.signBlob(
898-
crypto,
899-
client.getServiceAccountEmail() as string,
900-
data
901-
);
902-
}
903-
904896
const projectId = await this.getProjectId();
905897
if (!projectId) {
906898
throw new Error('Cannot sign data without a project ID.');

test/test.googleauth.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2239,7 +2239,7 @@ describe('googleauth', () => {
22392239

22402240
it('should use IAMCredentials endpoint when impersonation is used', async () => {
22412241
const scopes = mockGetAccessTokenAndProjectId(
2242-
false,
2242+
true,
22432243
['https://www.googleapis.com/auth/cloud-platform'],
22442244
true
22452245
);
@@ -2340,6 +2340,20 @@ describe('googleauth', () => {
23402340
assert.deepStrictEqual(res.data, data);
23412341
scopes.forEach(s => s.done());
23422342
});
2343+
2344+
describe('getCredentials()', () => {
2345+
it('getCredentials() should return the service account email for external accounts', async () => {
2346+
// Set up a mock to return path to a valid credentials file.
2347+
const email = saEmail;
2348+
const configWithImpersonation = createExternalAccountJSON();
2349+
configWithImpersonation.service_account_impersonation_url =
2350+
getServiceAccountImpersonationUrl();
2351+
const auth = new GoogleAuth({credentials: configWithImpersonation});
2352+
const body = await auth.getCredentials();
2353+
assert.notStrictEqual(null, body);
2354+
assert.strictEqual(email, body.client_email);
2355+
});
2356+
});
23432357
});
23442358
});
23452359

0 commit comments

Comments
 (0)