Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ export abstract class BaseExternalAccountClient extends AuthClient {
...BaseExternalAccountClient.RETRY_CONFIG,
headers,
url: `${this.cloudResourceManagerURL.toString()}${projectNumber}`,
responseType: 'json',
};
AuthClient.setMethodName(opts, 'getProjectId');
const response = await this.transporter.request<ProjectInfo>(opts);
Expand Down Expand Up @@ -669,6 +670,7 @@ export abstract class BaseExternalAccountClient extends AuthClient {
scope: this.getScopesArray(),
lifetime: this.serviceAccountImpersonationLifetime + 's',
},
responseType: 'json',
};
AuthClient.setMethodName(opts, 'getImpersonatedAccessToken');
const response =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export class DefaultAwsSecurityCredentialsSupplier
...this.additionalGaxiosOptions,
url: this.regionUrl,
method: 'GET',
responseType: 'text',
headers: metadataHeaders,
};
AuthClient.setMethodName(opts, 'getAwsRegion');
Expand Down Expand Up @@ -191,6 +192,7 @@ export class DefaultAwsSecurityCredentialsSupplier
...this.additionalGaxiosOptions,
url: this.imdsV2SessionTokenUrl,
method: 'PUT',
responseType: 'text',
headers: {'x-aws-ec2-metadata-token-ttl-seconds': '300'},
};
AuthClient.setMethodName(opts, '#getImdsV2SessionToken');
Expand Down Expand Up @@ -218,6 +220,7 @@ export class DefaultAwsSecurityCredentialsSupplier
...this.additionalGaxiosOptions,
url: this.securityCredentialsUrl,
method: 'GET',
responseType: 'text',
headers: headers,
};
AuthClient.setMethodName(opts, '#getAwsRoleName');
Expand All @@ -243,6 +246,7 @@ export class DefaultAwsSecurityCredentialsSupplier
...this.additionalGaxiosOptions,
url: `${this.securityCredentialsUrl}/${roleName}`,
headers: headers,
responseType: 'json',
} as GaxiosOptions;
AuthClient.setMethodName(opts, '#retrieveAwsSecurityCredentials');
const response =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class ExternalAccountAuthorizedUserHandler extends OAuthClientAuthHandler {
grant_type: 'refresh_token',
refresh_token: refreshToken,
}),
responseType: 'json',
};
AuthClient.setMethodName(opts, 'refreshToken');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export class UserRefreshClient extends OAuth2Client {
refresh_token: this._refreshToken,
target_audience: targetAudience,
} as {}),
responseType: 'json',
};
AuthClient.setMethodName(opts, 'fetchIdToken');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export class StsCredentials extends OAuthClientAuthHandler {
data: new URLSearchParams(
removeUndefinedValuesInObject(values) as Record<string, string>,
),
responseType: 'json',
};
AuthClient.setMethodName(opts, 'exchangeToken');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export class UrlSubjectTokenSupplier implements SubjectTokenSupplier {
url: this.url,
method: 'GET',
headers: this.headers,
responseType: this.formatType,
};
AuthClient.setMethodName(opts, 'getSubjectToken');

Expand Down
92 changes: 92 additions & 0 deletions packages/google-auth-library-nodejs/test/test.awsclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,98 @@ describe('AwsClient', () => {
scope.done();
});

it('should use correct responseType for metadata requests', async () => {
const scope = nock(metadataBaseUrl)
.get('/latest/meta-data/placement/availability-zone')
.reply(200, `${awsRegion}b`)
.get('/latest/meta-data/iam/security-credentials')
.reply(200, awsRole)
.get(`/latest/meta-data/iam/security-credentials/${awsRole}`)
.reply(200, awsSecurityCredentials);

const client = new AwsClient(awsOptions);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.retrieveSubjectToken();

// 1. GET /latest/meta-data/placement/availability-zone (Region)
assert.strictEqual(
requestSpy.getCall(0)!.args[0]!.responseType,
'text',
);
// 2. GET /latest/meta-data/iam/security-credentials (Role)
assert.strictEqual(
requestSpy.getCall(1)!.args[0]!.responseType,
'text',
);
// 3. GET /latest/meta-data/iam/security-credentials/{role} (Credentials)
assert.strictEqual(
requestSpy.getCall(2)!.args[0]!.responseType,
'json',
);

scope.done();
requestSpy.restore();
});

it('should use responseType: text for IMDSv2 session token', async () => {
const scopes: nock.Scope[] = [];
scopes.push(
nock(metadataBaseUrl, {
reqheaders: {'x-aws-ec2-metadata-token-ttl-seconds': '300'},
})
.put('/latest/api/token')
.twice()
.reply(200, awsSessionToken),
);

scopes.push(
nock(metadataBaseUrl, {
reqheaders: {'x-aws-ec2-metadata-token': awsSessionToken},
})
.get('/latest/meta-data/placement/availability-zone')
.reply(200, `${awsRegion}b`)
.get('/latest/meta-data/iam/security-credentials')
.reply(200, awsRole)
.get(`/latest/meta-data/iam/security-credentials/${awsRole}`)
.reply(200, awsSecurityCredentials),
);

const client = new AwsClient(awsOptionsWithImdsv2);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.retrieveSubjectToken();

// 1. PUT /latest/api/token (IMDSv2 session token for region)
assert.strictEqual(
requestSpy.getCall(0)!.args[0]!.responseType,
'text',
);
// 2. GET /latest/meta-data/placement/availability-zone (Region)
assert.strictEqual(
requestSpy.getCall(1)!.args[0]!.responseType,
'text',
);
// 3. PUT /latest/api/token (IMDSv2 session token for credentials)
assert.strictEqual(
requestSpy.getCall(2)!.args[0]!.responseType,
'text',
);
// 4. GET /latest/meta-data/iam/security-credentials (Role)
assert.strictEqual(
requestSpy.getCall(3)!.args[0]!.responseType,
'text',
);
// 5. GET /latest/meta-data/iam/security-credentials/{role} (Credentials)
assert.strictEqual(
requestSpy.getCall(4)!.args[0]!.responseType,
'json',
);

scopes.forEach(scope => scope.done());
requestSpy.restore();
});

it('should resolve on success with ipv6', async () => {
const ipv6baseUrl = 'http://[fd00:ec2::254]';
const ipv6CredentialSource = {
Expand Down
105 changes: 105 additions & 0 deletions packages/google-auth-library-nodejs/test/test.baseexternalclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,63 @@ describe('BaseExternalAccountClient', () => {
});

describe('getProjectId()', () => {
it('should use responseType: json', async () => {
const projectNumber = 'my-proj-number';
const projectId = 'my-proj-id';
const response = {
projectNumber,
projectId,
lifecycleState: 'ACTIVE',
name: 'project-name',
createTime: '2018-11-06T04:42:54.109Z',
parent: {
type: 'folder',
id: '12345678901',
},
};
const options = Object.assign({}, externalAccountOptions);
options.audience = getAudience(projectNumber);
const scopes = [
mockStsTokenExchange([
{
statusCode: 200,
response: stsSuccessfulResponse,
request: {
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
audience: options.audience,
scope: 'https://www.googleapis.com/auth/cloud-platform',
requested_token_type:
'urn:ietf:params:oauth:token-type:access_token',
subject_token: 'subject_token_0',
subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',
},
},
]),
mockCloudResourceManager(
projectNumber,
stsSuccessfulResponse.access_token,
200,
response,
),
];
const client = new TestExternalAccountClient(options);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.getProjectId();

const call = requestSpy
.getCalls()
.find(
c =>
c.args[0] &&
String((c.args[0] as any).url).includes('cloudresourcemanager'),
);
assert.ok(call);
assert.strictEqual((call!.args[0] as any).responseType, 'json');

scopes.forEach(scope => scope.done());
});

it('should resolve for workforce pools when workforce_pool_user_project is provided', async () => {
const options = Object.assign(
{},
Expand Down Expand Up @@ -1227,6 +1284,54 @@ describe('BaseExternalAccountClient', () => {
},
};

it('should use responseType: json for impersonation', async () => {
const scopes: nock.Scope[] = [];
scopes.push(
mockStsTokenExchange([
{
statusCode: 200,
response: stsSuccessfulResponse,
request: {
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
audience,
scope: 'https://www.googleapis.com/auth/cloud-platform',
requested_token_type:
'urn:ietf:params:oauth:token-type:access_token',
subject_token: 'subject_token_0',
subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',
},
},
]),
);
scopes.push(
mockGenerateAccessToken({
statusCode: 200,
response: saSuccessResponse,
token: stsSuccessfulResponse.access_token,
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
}),
);

const client = new TestExternalAccountClient(
externalAccountOptionsWithSA,
);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.getAccessToken();

const call = requestSpy
.getCalls()
.find(
c =>
c.args[0] &&
String((c.args[0] as any).url).includes('iamcredentials'),
);
assert.ok(call);
assert.strictEqual((call!.args[0] as any).responseType, 'json');

scopes.forEach(scope => scope.done());
});

it('should resolve with the expected response', async () => {
const scopes: nock.Scope[] = [];
scopes.push(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,33 @@ describe('ExternalAccountAuthorizedUserClient', () => {
});

describe('getAccessToken()', () => {
it('should use responseType: json', async () => {
const scope = mockStsTokenRefresh(BASE_URL, REFRESH_PATH, [
{
statusCode: 200,
response: successfulRefreshResponse,
request: {
grant_type: 'refresh_token',
refresh_token: 'refreshToken',
},
},
]);

const client = new ExternalAccountAuthorizedUserClient(
externalAccountAuthorizedUserCredentialOptions,
);
const requestSpy = sinon.spy(
(client as any).externalAccountAuthorizedUserHandler.transporter,
'request',
);

await client.getAccessToken();

const call = requestSpy.getCall(0);
assert.strictEqual(call.args[0]!.responseType, 'json');
scope.done();
});

it('should resolve with the expected response', async () => {
const scope = mockStsTokenRefresh(BASE_URL, REFRESH_PATH, [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,43 @@ describe('IdentityPoolClient', () => {

describe('for url-sourced subject tokens', () => {
describe('retrieveSubjectToken()', () => {
it('should use responseType: text for text format', async () => {
const externalSubjectToken = 'SUBJECT_TOKEN_1';
const scope = nock(metadataBaseUrl, {
reqheaders: metadataHeaders,
})
.get(metadataPath)
.reply(200, externalSubjectToken);

const client = new IdentityPoolClient(urlSourcedOptions);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.retrieveSubjectToken();

const call = requestSpy.getCall(0);
assert.strictEqual(call.args[0]!.responseType, 'text');
scope.done();
});
it('should use responseType: json for json format', async () => {
const externalSubjectToken = 'SUBJECT_TOKEN_1';
const jsonResponse = {
access_token: externalSubjectToken,
};
const scope = nock(metadataBaseUrl, {
reqheaders: metadataHeaders,
})
.get(metadataPath)
.reply(200, jsonResponse);

const client = new IdentityPoolClient(jsonRespUrlSourcedOptions);
const requestSpy = sinon.spy(client.transporter, 'request');

await client.retrieveSubjectToken();

const call = requestSpy.getCall(0);
assert.strictEqual(call.args[0]!.responseType, 'json');
scope.done();
});
it('should resolve on text response success', async () => {
const externalSubjectToken = 'SUBJECT_TOKEN_1';
const scope = nock(metadataBaseUrl, {
Expand Down
Loading
Loading