google-auth-library
Version:
Google APIs Authentication Client Library for Node.js
154 lines • 7.56 kB
JavaScript
"use strict";
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.AwsClient = void 0;
const awsrequestsigner_1 = require("./awsrequestsigner");
const baseexternalclient_1 = require("./baseexternalclient");
const defaultawssecuritycredentialssupplier_1 = require("./defaultawssecuritycredentialssupplier");
const util_1 = require("../util");
const gaxios_1 = require("gaxios");
/**
* AWS external account client. This is used for AWS workloads, where
* AWS STS GetCallerIdentity serialized signed requests are exchanged for
* GCP access token.
*/
class AwsClient extends baseexternalclient_1.BaseExternalAccountClient {
environmentId;
awsSecurityCredentialsSupplier;
regionalCredVerificationUrl;
awsRequestSigner;
region;
static #DEFAULT_AWS_REGIONAL_CREDENTIAL_VERIFICATION_URL = 'https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15';
/**
* @deprecated AWS client no validates the EC2 metadata address.
**/
static AWS_EC2_METADATA_IPV4_ADDRESS = '169.254.169.254';
/**
* @deprecated AWS client no validates the EC2 metadata address.
**/
static AWS_EC2_METADATA_IPV6_ADDRESS = 'fd00:ec2::254';
/**
* Instantiates an AwsClient instance using the provided JSON
* object loaded from an external account credentials file.
* An error is thrown if the credential is not a valid AWS credential.
* @param options The external account options object typically loaded
* from the external account JSON credential file.
*/
constructor(options) {
super(options);
const opts = (0, util_1.originalOrCamelOptions)(options);
const credentialSource = opts.get('credential_source');
const awsSecurityCredentialsSupplier = opts.get('aws_security_credentials_supplier');
// Validate credential sourcing configuration.
if (!credentialSource && !awsSecurityCredentialsSupplier) {
throw new Error('A credential source or AWS security credentials supplier must be specified.');
}
if (credentialSource && awsSecurityCredentialsSupplier) {
throw new Error('Only one of credential source or AWS security credentials supplier can be specified.');
}
if (awsSecurityCredentialsSupplier) {
this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier;
this.regionalCredVerificationUrl =
AwsClient.#DEFAULT_AWS_REGIONAL_CREDENTIAL_VERIFICATION_URL;
this.credentialSourceType = 'programmatic';
}
else {
const credentialSourceOpts = (0, util_1.originalOrCamelOptions)(credentialSource);
this.environmentId = credentialSourceOpts.get('environment_id');
// This is only required if the AWS region is not available in the
// AWS_REGION or AWS_DEFAULT_REGION environment variables.
const regionUrl = credentialSourceOpts.get('region_url');
// This is only required if AWS security credentials are not available in
// environment variables.
const securityCredentialsUrl = credentialSourceOpts.get('url');
const imdsV2SessionTokenUrl = credentialSourceOpts.get('imdsv2_session_token_url');
this.awsSecurityCredentialsSupplier =
new defaultawssecuritycredentialssupplier_1.DefaultAwsSecurityCredentialsSupplier({
regionUrl: regionUrl,
securityCredentialsUrl: securityCredentialsUrl,
imdsV2SessionTokenUrl: imdsV2SessionTokenUrl,
});
this.regionalCredVerificationUrl = credentialSourceOpts.get('regional_cred_verification_url');
this.credentialSourceType = 'aws';
// Data validators.
this.validateEnvironmentId();
}
this.awsRequestSigner = null;
this.region = '';
}
validateEnvironmentId() {
const match = this.environmentId?.match(/^(aws)(\d+)$/);
if (!match || !this.regionalCredVerificationUrl) {
throw new Error('No valid AWS "credential_source" provided');
}
else if (parseInt(match[2], 10) !== 1) {
throw new Error(`aws version "${match[2]}" is not supported in the current build.`);
}
}
/**
* Triggered when an external subject token is needed to be exchanged for a
* GCP access token via GCP STS endpoint. This will call the
* {@link AwsSecurityCredentialsSupplier} to retrieve an AWS region and AWS
* Security Credentials, then use them to create a signed AWS STS request that
* can be exchanged for a GCP access token.
* @return A promise that resolves with the external subject token.
*/
async retrieveSubjectToken() {
// Initialize AWS request signer if not already initialized.
if (!this.awsRequestSigner) {
this.region = await this.awsSecurityCredentialsSupplier.getAwsRegion(this.supplierContext);
this.awsRequestSigner = new awsrequestsigner_1.AwsRequestSigner(async () => {
return this.awsSecurityCredentialsSupplier.getAwsSecurityCredentials(this.supplierContext);
}, this.region);
}
// Generate signed request to AWS STS GetCallerIdentity API.
// Use the required regional endpoint. Otherwise, the request will fail.
const options = await this.awsRequestSigner.getRequestOptions({
...AwsClient.RETRY_CONFIG,
url: this.regionalCredVerificationUrl.replace('{region}', this.region),
method: 'POST',
});
// The GCP STS endpoint expects the headers to be formatted as:
// [
// {key: 'x-amz-date', value: '...'},
// {key: 'authorization', value: '...'},
// ...
// ]
// And then serialized as:
// encodeURIComponent(JSON.stringify({
// url: '...',
// method: 'POST',
// headers: [{key: 'x-amz-date', value: '...'}, ...]
// }))
const reformattedHeader = [];
const extendedHeaders = gaxios_1.Gaxios.mergeHeaders({
// The full, canonical resource name of the workload identity pool
// provider, with or without the HTTPS prefix.
// Including this header as part of the signature is recommended to
// ensure data integrity.
'x-goog-cloud-target-resource': this.audience,
}, options.headers);
// Reformat header to GCP STS expected format.
extendedHeaders.forEach((value, key) => reformattedHeader.push({ key, value }));
// Serialize the reformatted signed request.
return encodeURIComponent(JSON.stringify({
url: options.url,
method: options.method,
headers: reformattedHeader,
}));
}
}
exports.AwsClient = AwsClient;
//# sourceMappingURL=awsclient.js.map