670 lines
26 KiB
JavaScript
670 lines
26 KiB
JavaScript
"use strict";
|
|
// Copyright 2019 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.GoogleAuth = exports.CLOUD_SDK_CLIENT_ID = void 0;
|
|
const child_process_1 = require("child_process");
|
|
const fs = require("fs");
|
|
const gcpMetadata = require("gcp-metadata");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const crypto_1 = require("../crypto/crypto");
|
|
const transporters_1 = require("../transporters");
|
|
const computeclient_1 = require("./computeclient");
|
|
const idtokenclient_1 = require("./idtokenclient");
|
|
const envDetect_1 = require("./envDetect");
|
|
const jwtclient_1 = require("./jwtclient");
|
|
const refreshclient_1 = require("./refreshclient");
|
|
const externalclient_1 = require("./externalclient");
|
|
const baseexternalclient_1 = require("./baseexternalclient");
|
|
exports.CLOUD_SDK_CLIENT_ID = '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com';
|
|
class GoogleAuth {
|
|
constructor(opts) {
|
|
/**
|
|
* Caches a value indicating whether the auth layer is running on Google
|
|
* Compute Engine.
|
|
* @private
|
|
*/
|
|
this.checkIsGCE = undefined;
|
|
// To save the contents of the JSON credential file
|
|
this.jsonContent = null;
|
|
this.cachedCredential = null;
|
|
opts = opts || {};
|
|
this._cachedProjectId = opts.projectId || null;
|
|
this.keyFilename = opts.keyFilename || opts.keyFile;
|
|
this.scopes = opts.scopes;
|
|
this.jsonContent = opts.credentials || null;
|
|
this.clientOptions = opts.clientOptions;
|
|
}
|
|
// Note: this properly is only public to satisify unit tests.
|
|
// https://github.com/Microsoft/TypeScript/issues/5228
|
|
get isGCE() {
|
|
return this.checkIsGCE;
|
|
}
|
|
// GAPIC client libraries should always use self-signed JWTs. The following
|
|
// variables are set on the JWT client in order to indicate the type of library,
|
|
// and sign the JWT with the correct audience and scopes (if not supplied).
|
|
setGapicJWTValues(client) {
|
|
client.defaultServicePath = this.defaultServicePath;
|
|
client.useJWTAccessWithScope = this.useJWTAccessWithScope;
|
|
client.defaultScopes = this.defaultScopes;
|
|
}
|
|
getProjectId(callback) {
|
|
if (callback) {
|
|
this.getProjectIdAsync().then(r => callback(null, r), callback);
|
|
}
|
|
else {
|
|
return this.getProjectIdAsync();
|
|
}
|
|
}
|
|
getProjectIdAsync() {
|
|
if (this._cachedProjectId) {
|
|
return Promise.resolve(this._cachedProjectId);
|
|
}
|
|
// In implicit case, supports three environments. In order of precedence,
|
|
// the implicit environments are:
|
|
// - GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variable
|
|
// - GOOGLE_APPLICATION_CREDENTIALS JSON file
|
|
// - Cloud SDK: `gcloud config config-helper --format json`
|
|
// - GCE project ID from metadata server)
|
|
if (!this._getDefaultProjectIdPromise) {
|
|
// TODO: refactor the below code so that it doesn't mix and match
|
|
// promises and async/await.
|
|
this._getDefaultProjectIdPromise = new Promise(
|
|
// eslint-disable-next-line no-async-promise-executor
|
|
async (resolve, reject) => {
|
|
try {
|
|
const projectId = this.getProductionProjectId() ||
|
|
(await this.getFileProjectId()) ||
|
|
(await this.getDefaultServiceProjectId()) ||
|
|
(await this.getGCEProjectId()) ||
|
|
(await this.getExternalAccountClientProjectId());
|
|
this._cachedProjectId = projectId;
|
|
if (!projectId) {
|
|
throw new Error('Unable to detect a Project Id in the current environment. \n' +
|
|
'To learn more about authentication and Google APIs, visit: \n' +
|
|
'https://cloud.google.com/docs/authentication/getting-started');
|
|
}
|
|
resolve(projectId);
|
|
}
|
|
catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
return this._getDefaultProjectIdPromise;
|
|
}
|
|
/**
|
|
* @returns Any scopes (user-specified or default scopes specified by the
|
|
* client library) that need to be set on the current Auth client.
|
|
*/
|
|
getAnyScopes() {
|
|
return this.scopes || this.defaultScopes;
|
|
}
|
|
getApplicationDefault(optionsOrCallback = {}, callback) {
|
|
let options;
|
|
if (typeof optionsOrCallback === 'function') {
|
|
callback = optionsOrCallback;
|
|
}
|
|
else {
|
|
options = optionsOrCallback;
|
|
}
|
|
if (callback) {
|
|
this.getApplicationDefaultAsync(options).then(r => callback(null, r.credential, r.projectId), callback);
|
|
}
|
|
else {
|
|
return this.getApplicationDefaultAsync(options);
|
|
}
|
|
}
|
|
async getApplicationDefaultAsync(options = {}) {
|
|
// If we've already got a cached credential, just return it.
|
|
if (this.cachedCredential) {
|
|
return {
|
|
credential: this.cachedCredential,
|
|
projectId: await this.getProjectIdAsync(),
|
|
};
|
|
}
|
|
let credential;
|
|
let projectId;
|
|
// Check for the existence of a local environment variable pointing to the
|
|
// location of the credential file. This is typically used in local
|
|
// developer scenarios.
|
|
credential =
|
|
await this._tryGetApplicationCredentialsFromEnvironmentVariable(options);
|
|
if (credential) {
|
|
if (credential instanceof jwtclient_1.JWT) {
|
|
credential.scopes = this.scopes;
|
|
}
|
|
else if (credential instanceof baseexternalclient_1.BaseExternalAccountClient) {
|
|
credential.scopes = this.getAnyScopes();
|
|
}
|
|
this.cachedCredential = credential;
|
|
projectId = await this.getProjectId();
|
|
return { credential, projectId };
|
|
}
|
|
// Look in the well-known credential file location.
|
|
credential = await this._tryGetApplicationCredentialsFromWellKnownFile(options);
|
|
if (credential) {
|
|
if (credential instanceof jwtclient_1.JWT) {
|
|
credential.scopes = this.scopes;
|
|
}
|
|
else if (credential instanceof baseexternalclient_1.BaseExternalAccountClient) {
|
|
credential.scopes = this.getAnyScopes();
|
|
}
|
|
this.cachedCredential = credential;
|
|
projectId = await this.getProjectId();
|
|
return { credential, projectId };
|
|
}
|
|
// Determine if we're running on GCE.
|
|
let isGCE;
|
|
try {
|
|
isGCE = await this._checkIsGCE();
|
|
}
|
|
catch (e) {
|
|
e.message = `Unexpected error determining execution environment: ${e.message}`;
|
|
throw e;
|
|
}
|
|
if (!isGCE) {
|
|
// We failed to find the default credentials. Bail out with an error.
|
|
throw new Error('Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.');
|
|
}
|
|
// For GCE, just return a default ComputeClient. It will take care of
|
|
// the rest.
|
|
options.scopes = this.getAnyScopes();
|
|
this.cachedCredential = new computeclient_1.Compute(options);
|
|
projectId = await this.getProjectId();
|
|
return { projectId, credential: this.cachedCredential };
|
|
}
|
|
/**
|
|
* Determines whether the auth layer is running on Google Compute Engine.
|
|
* @returns A promise that resolves with the boolean.
|
|
* @api private
|
|
*/
|
|
async _checkIsGCE() {
|
|
if (this.checkIsGCE === undefined) {
|
|
this.checkIsGCE = await gcpMetadata.isAvailable();
|
|
}
|
|
return this.checkIsGCE;
|
|
}
|
|
/**
|
|
* Attempts to load default credentials from the environment variable path..
|
|
* @returns Promise that resolves with the OAuth2Client or null.
|
|
* @api private
|
|
*/
|
|
async _tryGetApplicationCredentialsFromEnvironmentVariable(options) {
|
|
const credentialsPath = process.env['GOOGLE_APPLICATION_CREDENTIALS'] ||
|
|
process.env['google_application_credentials'];
|
|
if (!credentialsPath || credentialsPath.length === 0) {
|
|
return null;
|
|
}
|
|
try {
|
|
return this._getApplicationCredentialsFromFilePath(credentialsPath, options);
|
|
}
|
|
catch (e) {
|
|
e.message = `Unable to read the credential file specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable: ${e.message}`;
|
|
throw e;
|
|
}
|
|
}
|
|
/**
|
|
* Attempts to load default credentials from a well-known file location
|
|
* @return Promise that resolves with the OAuth2Client or null.
|
|
* @api private
|
|
*/
|
|
async _tryGetApplicationCredentialsFromWellKnownFile(options) {
|
|
// First, figure out the location of the file, depending upon the OS type.
|
|
let location = null;
|
|
if (this._isWindows()) {
|
|
// Windows
|
|
location = process.env['APPDATA'];
|
|
}
|
|
else {
|
|
// Linux or Mac
|
|
const home = process.env['HOME'];
|
|
if (home) {
|
|
location = path.join(home, '.config');
|
|
}
|
|
}
|
|
// If we found the root path, expand it.
|
|
if (location) {
|
|
location = path.join(location, 'gcloud', 'application_default_credentials.json');
|
|
if (!fs.existsSync(location)) {
|
|
location = null;
|
|
}
|
|
}
|
|
// The file does not exist.
|
|
if (!location) {
|
|
return null;
|
|
}
|
|
// The file seems to exist. Try to use it.
|
|
const client = await this._getApplicationCredentialsFromFilePath(location, options);
|
|
return client;
|
|
}
|
|
/**
|
|
* Attempts to load default credentials from a file at the given path..
|
|
* @param filePath The path to the file to read.
|
|
* @returns Promise that resolves with the OAuth2Client
|
|
* @api private
|
|
*/
|
|
async _getApplicationCredentialsFromFilePath(filePath, options = {}) {
|
|
// Make sure the path looks like a string.
|
|
if (!filePath || filePath.length === 0) {
|
|
throw new Error('The file path is invalid.');
|
|
}
|
|
// Make sure there is a file at the path. lstatSync will throw if there is
|
|
// nothing there.
|
|
try {
|
|
// Resolve path to actual file in case of symlink. Expect a thrown error
|
|
// if not resolvable.
|
|
filePath = fs.realpathSync(filePath);
|
|
if (!fs.lstatSync(filePath).isFile()) {
|
|
throw new Error();
|
|
}
|
|
}
|
|
catch (err) {
|
|
err.message = `The file at ${filePath} does not exist, or it is not a file. ${err.message}`;
|
|
throw err;
|
|
}
|
|
// Now open a read stream on the file, and parse it.
|
|
const readStream = fs.createReadStream(filePath);
|
|
return this.fromStream(readStream, options);
|
|
}
|
|
/**
|
|
* Create a credentials instance using the given input options.
|
|
* @param json The input object.
|
|
* @param options The JWT or UserRefresh options for the client
|
|
* @returns JWT or UserRefresh Client with data
|
|
*/
|
|
fromJSON(json, options) {
|
|
let client;
|
|
if (!json) {
|
|
throw new Error('Must pass in a JSON object containing the Google auth settings.');
|
|
}
|
|
options = options || {};
|
|
if (json.type === 'authorized_user') {
|
|
client = new refreshclient_1.UserRefreshClient(options);
|
|
client.fromJSON(json);
|
|
}
|
|
else if (json.type === baseexternalclient_1.EXTERNAL_ACCOUNT_TYPE) {
|
|
client = externalclient_1.ExternalAccountClient.fromJSON(json, options);
|
|
client.scopes = this.getAnyScopes();
|
|
}
|
|
else {
|
|
options.scopes = this.scopes;
|
|
client = new jwtclient_1.JWT(options);
|
|
this.setGapicJWTValues(client);
|
|
client.fromJSON(json);
|
|
}
|
|
return client;
|
|
}
|
|
/**
|
|
* Return a JWT or UserRefreshClient from JavaScript object, caching both the
|
|
* object used to instantiate and the client.
|
|
* @param json The input object.
|
|
* @param options The JWT or UserRefresh options for the client
|
|
* @returns JWT or UserRefresh Client with data
|
|
*/
|
|
_cacheClientFromJSON(json, options) {
|
|
let client;
|
|
// create either a UserRefreshClient or JWT client.
|
|
options = options || {};
|
|
if (json.type === 'authorized_user') {
|
|
client = new refreshclient_1.UserRefreshClient(options);
|
|
client.fromJSON(json);
|
|
}
|
|
else if (json.type === baseexternalclient_1.EXTERNAL_ACCOUNT_TYPE) {
|
|
client = externalclient_1.ExternalAccountClient.fromJSON(json, options);
|
|
client.scopes = this.getAnyScopes();
|
|
}
|
|
else {
|
|
options.scopes = this.scopes;
|
|
client = new jwtclient_1.JWT(options);
|
|
this.setGapicJWTValues(client);
|
|
client.fromJSON(json);
|
|
}
|
|
// cache both raw data used to instantiate client and client itself.
|
|
this.jsonContent = json;
|
|
this.cachedCredential = client;
|
|
return this.cachedCredential;
|
|
}
|
|
fromStream(inputStream, optionsOrCallback = {}, callback) {
|
|
let options = {};
|
|
if (typeof optionsOrCallback === 'function') {
|
|
callback = optionsOrCallback;
|
|
}
|
|
else {
|
|
options = optionsOrCallback;
|
|
}
|
|
if (callback) {
|
|
this.fromStreamAsync(inputStream, options).then(r => callback(null, r), callback);
|
|
}
|
|
else {
|
|
return this.fromStreamAsync(inputStream, options);
|
|
}
|
|
}
|
|
fromStreamAsync(inputStream, options) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!inputStream) {
|
|
throw new Error('Must pass in a stream containing the Google auth settings.');
|
|
}
|
|
let s = '';
|
|
inputStream
|
|
.setEncoding('utf8')
|
|
.on('error', reject)
|
|
.on('data', chunk => (s += chunk))
|
|
.on('end', () => {
|
|
try {
|
|
try {
|
|
const data = JSON.parse(s);
|
|
const r = this._cacheClientFromJSON(data, options);
|
|
return resolve(r);
|
|
}
|
|
catch (err) {
|
|
// If we failed parsing this.keyFileName, assume that it
|
|
// is a PEM or p12 certificate:
|
|
if (!this.keyFilename)
|
|
throw err;
|
|
const client = new jwtclient_1.JWT({
|
|
...this.clientOptions,
|
|
keyFile: this.keyFilename,
|
|
});
|
|
this.cachedCredential = client;
|
|
this.setGapicJWTValues(client);
|
|
return resolve(client);
|
|
}
|
|
}
|
|
catch (err) {
|
|
return reject(err);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Create a credentials instance using the given API key string.
|
|
* @param apiKey The API key string
|
|
* @param options An optional options object.
|
|
* @returns A JWT loaded from the key
|
|
*/
|
|
fromAPIKey(apiKey, options) {
|
|
options = options || {};
|
|
const client = new jwtclient_1.JWT(options);
|
|
client.fromAPIKey(apiKey);
|
|
return client;
|
|
}
|
|
/**
|
|
* Determines whether the current operating system is Windows.
|
|
* @api private
|
|
*/
|
|
_isWindows() {
|
|
const sys = os.platform();
|
|
if (sys && sys.length >= 3) {
|
|
if (sys.substring(0, 3).toLowerCase() === 'win') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Run the Google Cloud SDK command that prints the default project ID
|
|
*/
|
|
async getDefaultServiceProjectId() {
|
|
return new Promise(resolve => {
|
|
child_process_1.exec('gcloud config config-helper --format json', (err, stdout) => {
|
|
if (!err && stdout) {
|
|
try {
|
|
const projectId = JSON.parse(stdout).configuration.properties.core.project;
|
|
resolve(projectId);
|
|
return;
|
|
}
|
|
catch (e) {
|
|
// ignore errors
|
|
}
|
|
}
|
|
resolve(null);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Loads the project id from environment variables.
|
|
* @api private
|
|
*/
|
|
getProductionProjectId() {
|
|
return (process.env['GCLOUD_PROJECT'] ||
|
|
process.env['GOOGLE_CLOUD_PROJECT'] ||
|
|
process.env['gcloud_project'] ||
|
|
process.env['google_cloud_project']);
|
|
}
|
|
/**
|
|
* Loads the project id from the GOOGLE_APPLICATION_CREDENTIALS json file.
|
|
* @api private
|
|
*/
|
|
async getFileProjectId() {
|
|
if (this.cachedCredential) {
|
|
// Try to read the project ID from the cached credentials file
|
|
return this.cachedCredential.projectId;
|
|
}
|
|
// Ensure the projectId is loaded from the keyFile if available.
|
|
if (this.keyFilename) {
|
|
const creds = await this.getClient();
|
|
if (creds && creds.projectId) {
|
|
return creds.projectId;
|
|
}
|
|
}
|
|
// Try to load a credentials file and read its project ID
|
|
const r = await this._tryGetApplicationCredentialsFromEnvironmentVariable();
|
|
if (r) {
|
|
return r.projectId;
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Gets the project ID from external account client if available.
|
|
*/
|
|
async getExternalAccountClientProjectId() {
|
|
if (!this.jsonContent || this.jsonContent.type !== baseexternalclient_1.EXTERNAL_ACCOUNT_TYPE) {
|
|
return null;
|
|
}
|
|
const creds = await this.getClient();
|
|
// Do not suppress the underlying error, as the error could contain helpful
|
|
// information for debugging and fixing. This is especially true for
|
|
// external account creds as in order to get the project ID, the following
|
|
// operations have to succeed:
|
|
// 1. Valid credentials file should be supplied.
|
|
// 2. Ability to retrieve access tokens from STS token exchange API.
|
|
// 3. Ability to exchange for service account impersonated credentials (if
|
|
// enabled).
|
|
// 4. Ability to get project info using the access token from step 2 or 3.
|
|
// Without surfacing the error, it is harder for developers to determine
|
|
// which step went wrong.
|
|
return await creds.getProjectId();
|
|
}
|
|
/**
|
|
* Gets the Compute Engine project ID if it can be inferred.
|
|
*/
|
|
async getGCEProjectId() {
|
|
try {
|
|
const r = await gcpMetadata.project('project-id');
|
|
return r;
|
|
}
|
|
catch (e) {
|
|
// Ignore any errors
|
|
return null;
|
|
}
|
|
}
|
|
getCredentials(callback) {
|
|
if (callback) {
|
|
this.getCredentialsAsync().then(r => callback(null, r), callback);
|
|
}
|
|
else {
|
|
return this.getCredentialsAsync();
|
|
}
|
|
}
|
|
async getCredentialsAsync() {
|
|
await this.getClient();
|
|
if (this.jsonContent) {
|
|
const credential = {
|
|
client_email: this.jsonContent.client_email,
|
|
private_key: this.jsonContent.private_key,
|
|
};
|
|
return credential;
|
|
}
|
|
const isGCE = await this._checkIsGCE();
|
|
if (!isGCE) {
|
|
throw new Error('Unknown error.');
|
|
}
|
|
// For GCE, return the service account details from the metadata server
|
|
// NOTE: The trailing '/' at the end of service-accounts/ is very important!
|
|
// The GCF metadata server doesn't respect querystring params if this / is
|
|
// not included.
|
|
const data = await gcpMetadata.instance({
|
|
property: 'service-accounts/',
|
|
params: { recursive: 'true' },
|
|
});
|
|
if (!data || !data.default || !data.default.email) {
|
|
throw new Error('Failure from metadata server.');
|
|
}
|
|
return { client_email: data.default.email };
|
|
}
|
|
/**
|
|
* Automatically obtain a client based on the provided configuration. If no
|
|
* options were passed, use Application Default Credentials.
|
|
*/
|
|
async getClient(options) {
|
|
if (options) {
|
|
throw new Error('Passing options to getClient is forbidden in v5.0.0. Use new GoogleAuth(opts) instead.');
|
|
}
|
|
if (!this.cachedCredential) {
|
|
if (this.jsonContent) {
|
|
this._cacheClientFromJSON(this.jsonContent, this.clientOptions);
|
|
}
|
|
else if (this.keyFilename) {
|
|
const filePath = path.resolve(this.keyFilename);
|
|
const stream = fs.createReadStream(filePath);
|
|
await this.fromStreamAsync(stream, this.clientOptions);
|
|
}
|
|
else {
|
|
await this.getApplicationDefaultAsync(this.clientOptions);
|
|
}
|
|
}
|
|
return this.cachedCredential;
|
|
}
|
|
/**
|
|
* Creates a client which will fetch an ID token for authorization.
|
|
* @param targetAudience the audience for the fetched ID token.
|
|
* @returns IdTokenClient for making HTTP calls authenticated with ID tokens.
|
|
*/
|
|
async getIdTokenClient(targetAudience) {
|
|
const client = await this.getClient();
|
|
if (!('fetchIdToken' in client)) {
|
|
throw new Error('Cannot fetch ID token in this environment, use GCE or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to a service account credentials JSON file.');
|
|
}
|
|
return new idtokenclient_1.IdTokenClient({ targetAudience, idTokenProvider: client });
|
|
}
|
|
/**
|
|
* Automatically obtain application default credentials, and return
|
|
* an access token for making requests.
|
|
*/
|
|
async getAccessToken() {
|
|
const client = await this.getClient();
|
|
return (await client.getAccessToken()).token;
|
|
}
|
|
/**
|
|
* Obtain the HTTP headers that will provide authorization for a given
|
|
* request.
|
|
*/
|
|
async getRequestHeaders(url) {
|
|
const client = await this.getClient();
|
|
return client.getRequestHeaders(url);
|
|
}
|
|
/**
|
|
* Obtain credentials for a request, then attach the appropriate headers to
|
|
* the request options.
|
|
* @param opts Axios or Request options on which to attach the headers
|
|
*/
|
|
async authorizeRequest(opts) {
|
|
opts = opts || {};
|
|
const url = opts.url || opts.uri;
|
|
const client = await this.getClient();
|
|
const headers = await client.getRequestHeaders(url);
|
|
opts.headers = Object.assign(opts.headers || {}, headers);
|
|
return opts;
|
|
}
|
|
/**
|
|
* Automatically obtain application default credentials, and make an
|
|
* HTTP request using the given options.
|
|
* @param opts Axios request options for the HTTP request.
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
async request(opts) {
|
|
const client = await this.getClient();
|
|
return client.request(opts);
|
|
}
|
|
/**
|
|
* Determine the compute environment in which the code is running.
|
|
*/
|
|
getEnv() {
|
|
return envDetect_1.getEnv();
|
|
}
|
|
/**
|
|
* Sign the given data with the current private key, or go out
|
|
* to the IAM API to sign it.
|
|
* @param data The data to be signed.
|
|
*/
|
|
async sign(data) {
|
|
const client = await this.getClient();
|
|
const crypto = crypto_1.createCrypto();
|
|
if (client instanceof jwtclient_1.JWT && client.key) {
|
|
const sign = await crypto.sign(client.key, data);
|
|
return sign;
|
|
}
|
|
// signBlob requires a service account email and the underlying
|
|
// access token to have iam.serviceAccounts.signBlob permission
|
|
// on the specified resource name.
|
|
// The "Service Account Token Creator" role should cover this.
|
|
// As a result external account credentials can support this
|
|
// operation when service account impersonation is enabled.
|
|
if (client instanceof baseexternalclient_1.BaseExternalAccountClient &&
|
|
client.getServiceAccountEmail()) {
|
|
return this.signBlob(crypto, client.getServiceAccountEmail(), data);
|
|
}
|
|
const projectId = await this.getProjectId();
|
|
if (!projectId) {
|
|
throw new Error('Cannot sign data without a project ID.');
|
|
}
|
|
const creds = await this.getCredentials();
|
|
if (!creds.client_email) {
|
|
throw new Error('Cannot sign data without `client_email`.');
|
|
}
|
|
return this.signBlob(crypto, creds.client_email, data);
|
|
}
|
|
async signBlob(crypto, emailOrUniqueId, data) {
|
|
const url = 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/' +
|
|
`${emailOrUniqueId}:signBlob`;
|
|
const res = await this.request({
|
|
method: 'POST',
|
|
url,
|
|
data: {
|
|
payload: crypto.encodeBase64StringUtf8(data),
|
|
},
|
|
});
|
|
return res.data.signedBlob;
|
|
}
|
|
}
|
|
exports.GoogleAuth = GoogleAuth;
|
|
/**
|
|
* Export DefaultTransporter as a static property of the class.
|
|
*/
|
|
GoogleAuth.DefaultTransporter = transporters_1.DefaultTransporter;
|
|
//# sourceMappingURL=googleauth.js.map
|