AWS sdk get credentials using sso - node.js

I'm trying to use the AWS secrets manager, when I'm using regular credentials its works fine.
but I want to use SSO for it. when I don't have the .aws/credentials file and only .aws/config file.
In AWS documentation i saw this functions:
var params = {
accessToken: 'STRING_VALUE', /* required */
accountId: 'STRING_VALUE', /* required */
roleName: 'STRING_VALUE' /* required */
};
sso.getRoleCredentials(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
but I don't understand where I can get the access token, account id, and role name.
indeed I saw that in the .aws/config file there is an account id and role name, but I don't understand how can I get them into my code (maybe something like the function SharedIniFileCredentials) and also how can I get the access token?
Also,
I tried to add this env variable AWS_SDK_LOAD_CONFIG=1 but still I'm getting this error:
Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1

You can do this with credential-provider-sso. The documentation gives a full explanation but a quick example:
const { SecretsManager } = require('#aws-sdk/client-secrets-manager');
const { fromSSO } = require('#aws-sdk/credential-provider-sso');
const secretsManager = new SecretsManager({credentials: fromSSO()});
secretsManager.listSecrets({}, (err, data) => {
console.error(err);
console.info(data);
})
This will use the profile configured in the AWS_PROFILE environment variable, or you can pass {'profile': 'profilename'} to the fromSSO function.

Related

Could not load the default credentials? (Node.js Google Compute Engine)

I am trying to create a new vm using Nodejs client libraries of GCP, I followed the below link,
https://googleapis.dev/nodejs/compute/latest/VM.html#create
and below is my code
const Compute = require('#google-cloud/compute');
const {auth} = require('google-auth-library');
const compute = new Compute();
var cred = "<<<credential json content as string>>>";
auth.scopes = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/compute'];
auth.jsonContent = JSON.parse(cred);
const config = {
machineType: 'n1-standard-1',
disks: [ {
boot: true,
initializeParams: { sourceImage: '<<<image url>>>' }
} ],
networkInterfaces: [ { network: 'global/networks/default' } ],
tags: [ { items: [ 'debian-server', 'http-server' ] } ],
auth: auth,
};
async function main() {
// [START gce_create_vm]
async function createVM() {
const zone = compute.zone('us-central1-c');
const vm = zone.vm('vm-name');
await vm.create(config).then(function(data) {
const vm = data[0];
const operation = data[1];
const apiResponse = data[2];
});
console.log(vm);
console.log('Virtual machine created!');
}
createVM().catch(function (err) {
console.log(err);
});
// [END gce_create_vm]
}
main();
when i run this, the error I am getting is
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
at GoogleAuth.getApplicationDefaultAsync (D:\Click to deploy\src\c2dNodeGCP\node_modules\google-auth-library\build\src\auth\googleauth.js:155:19)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async GoogleAuth.getClient (D:\Click to deploy\src\c2dNodeGCP\node_modules\google-auth-library\build\src\auth\googleauth.js:487:17)
at async GoogleAuth.authorizeRequest (D:\Click to deploy\src\c2dNodeGCP\node_modules\google-auth-library\build\src\auth\googleauth.js:528:24)
My scenario is to take the service account credential from string variable rather than from env var or some other thing.
I can see that it is trying to take the default credential which is not there in my case.
I was able to achieve this in java, but here i am not able to do it. Any help will be appreciated.
In order to execute your local application using your own user credentials for API access temporarily you can run:
gcloud auth application-default login
You have to install sdk into your computer, that will enable you to run the code.
Then log in to your associated gmail account and you will be ready.
You can check the following documentation, to get more information.
Another option is to set GOOGLE_APPLICATION_CREDENTIALS to provide authentication credentials to your application code. It should point to a file that defines the credentials.
To get this file please follow the steps:
Navigate to the APIs & Services→Credentials panel in Cloud Console.
Select Create credentials, then select API key from the dropdown menu.
The API key created dialog box displays your newly created key.
You might want to copy your key and keep it secure. Unless you are using a testing key that you intend to delete later.
Put the *.json file you just downloaded in a directory of your choosing.
This directory must be private (you can't let anyone get access to this), but accessible to your web server code.
You can write your own code to pass the service account key to the client library or set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the path of the JSON file downloaded.
I have found the following code that explains how you can authenticate to Google Cloud Platform APIs using the Google Cloud Client Libraries.
/**
* Demonstrates how to authenticate to Google Cloud Platform APIs using the
* Google Cloud Client Libraries.
*/
'use strict';
const authCloudImplicit = async () => {
// [START auth_cloud_implicit]
// Imports the Google Cloud client library.
const {Storage} = require('#google-cloud/storage');
// Instantiates a client. If you don't specify credentials when constructing
// the client, the client library will look for credentials in the
// environment.
const storage = new Storage();
// Makes an authenticated API request.
async function listBuckets() {
try {
const results = await storage.getBuckets();
const [buckets] = results;
console.log('Buckets:');
buckets.forEach((bucket) => {
console.log(bucket.name);
});
} catch (err) {
console.error('ERROR:', err);
}
}
listBuckets();
// [END auth_cloud_implicit]
};
const authCloudExplicit = async ({projectId, keyFilename}) => {
// [START auth_cloud_explicit]
// Imports the Google Cloud client library.
const {Storage} = require('#google-cloud/storage');
// Instantiates a client. Explicitly use service account credentials by
// specifying the private key file. All clients in google-cloud-node have this
// helper, see https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md
// const projectId = 'project-id'
// const keyFilename = '/path/to/keyfile.json'
const storage = new Storage({projectId, keyFilename});
// Makes an authenticated API request.
async function listBuckets() {
try {
const [buckets] = await storage.getBuckets();
console.log('Buckets:');
buckets.forEach((bucket) => {
console.log(bucket.name);
});
} catch (err) {
console.error('ERROR:', err);
}
}
listBuckets();
// [END auth_cloud_explicit]
};
const cli = require(`yargs`)
.demand(1)
.command(
`auth-cloud-implicit`,
`Loads credentials implicitly.`,
{},
authCloudImplicit
)
.command(
`auth-cloud-explicit`,
`Loads credentials explicitly.`,
{
projectId: {
alias: 'p',
default: process.env.GOOGLE_CLOUD_PROJECT,
},
keyFilename: {
alias: 'k',
default: process.env.GOOGLE_APPLICATION_CREDENTIALS,
},
},
authCloudExplicit
)
.example(`node $0 implicit`, `Loads credentials implicitly.`)
.example(`node $0 explicit`, `Loads credentials explicitly.`)
.wrap(120)
.recommendCommands()
.epilogue(
`For more information, see https://cloud.google.com/docs/authentication`
)
.help()
.strict();
if (module === require.main) {
cli.parse(process.argv.slice(2));
}
You could obtain more information about this in this link, also you can take a look at this other guide for Getting started with authentication.
Edit 1
To load your credentials from a local file you can use something like:
const Compute = require('#google-cloud/compute');
const compute = new Compute({
projectId: 'your-project-id',
keyFilename: '/path/to/keyfile.json'
});
You can check this link for more examples and information.
This other link contains another example that could be useful.

How to grab value from promise in Nodejs

Hi I am writing a nodejs code in Azure functions to capture the username saved in Azure key vault.
Here is the code I have written
module.exports = async function (context, req) {
var msRestAzure = require('ms-rest-azure');
var KeyVault = require('azure-keyvault');
function getKeyVaultCredentials() {
return msRestAzure.loginWithAppServiceMSI({
resource: 'https://vault.azure.net/'
});
}
function getKeyVaultSecret(credentials) {
let keyVaultClient = new KeyVault.KeyVaultClient(credentials);
return keyVaultClient.getSecret('https://myDNS.vault.azure.net/', 'username', '');
}
const username = getKeyVaultCredentials()
.then(getKeyVaultSecret)
.then(function (secret){
context.log(`Your secret value is: ${secret.value}.`);
return secret.value;})
.catch(function (err) {throw (err);});
context.log(username)
context.res = {
body: username
};
}
I want to capture the username but it is giving me output as
promise {pending}
How to wait for the function to end so that I can extract the username.
I am very new nodejs, please let me know what wrong I am doing and what should be the exact solution.
Thanks
Actually you have already used then to get the secret value. The value will be returned if there is no issues with the dependencies and configurations.
getKeyVaultCredentials()
.then(getKeyVaultSecret)
.then(function (secret){
context.log(`Your secret value is: ${secret.value}.`);
return secret.value;})
.catch(function (err) {throw (err);});
But you will encounter some issues when using this sdk and here is the github issue for your reference.
It is recommended that you use the new Azure Key Vault SDK instead. It is more convenient to use. Here are the detailed steps to use MSI and Key vault in Azure function.

Error 'Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup' on API call

Trying to make my expressJS app control users' google drive files. I've been utilizing the node 'googleapis' package. After following a slightly dated/incorrect article here, I've gotten the script to:
redirect a user to their authorization url
grab the 'code' from get parameter and then...
register it back as access tokens, which can then be used to...
create a registered 'auth' object
When I use this to create the drive object and try to get it to list files, I get the following error: 'Error: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup'
This error has already popped up on SO question, and on github.
Following general advice, I re-enabled the drive api, re-downloaded access key. I've also tried replacing the sensitive drive scope a gmail scope, but that didn't work either. I'm not sure where else turn to start debugging at this point. I have a sneaking suspicion my entire auth object is being formed incorrectly but I can't find anything wrong.
This is the related piece of Express app code I'm using to create the authObject and then read drive files.
/**
* Google Utility class that packages different actions related to authentication
*/
class GoogleUtil {
constructor(secretFileName = 'client_secret.json') {
const secret = JSON.parse(fs.readFileSync(secretFileName)).web;
const { client_id, client_secret, redirect_uris } = secret;
this.client_id = client_id;
this.client_secret = client_secret;
this.redirect_uri = redirect_uris[0];
this.standardScope = [
'https://www.googleapis.com/auth/drive',
// 'https://www.googleapis.com/auth/gmail.readonly',
// 'https://www.googleapis.com/auth/userinfo.profile'
];
}
createConnection() {
return new google.auth.OAuth2(this.client_id, this.client_secret, this.redirect_uri); // form authObject
}
getConnectionUrl(auth) {
return auth.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: this.standardScope
});
}
async getAccessTokenFromCode(code) {
var auth = this.createConnection();
const data = await auth.getToken(code);
return data.tokens;
}
}
const g = new GoogleUtil();
/**
* BEGIN ROUTE DEFINITIONS
*/
// THIS IS FIRST STEP. FRONTEND WILL REDIRECT TO GIVEN URL
app.get('/api/googleLoginUrl', async (req, res) => {
const oAuth2Client = g.createConnection();
const url = g.getConnectionUrl(oAuth2Client);
res.json({ url });
});
// *****
// NOTE: THIS IS ROUTE THAT ATTEMPTS TO LIST FILES AND THROWS ERROR
// *****
app.get('/google-auth-redirect', async (req, res) => {
if (!req.query.code) return res.send('Malformed request');
const tokens = await g.getAccessTokenFromCode(req.query.code);
const auth = g.createConnection().setCredentials(tokens);
const drive = google.drive({ version: 'v3', auth: auth });
drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
}, (err, resp) => {
if (err) return console.log('The API returned an error: ' + err);
console.log(resp);
});
res.redirect('/');
});
In the google developer console, clicking on 'create credentials' in the drive API overview informs me that my current credentials are compatible. The project scopes do include ' ../auth/drive'.
I'd want it to be able to list files from an authenticated user's account.
I think this might be related to how you are asking for permissions. If you are using your application to manipulate user's drive files you need a couple of things:
Check you have the correct access scopes setup.
Check you authentication parameter/Oauth screen is setup with said scopes.
You might want to read some documentation regarding authorizing users
Hope this helps! ;)

How can I change a VM/Instance label with GCP Cloud Function using node.js?

I am testing a Cloud Function in GCP and I want to modify labels of my compute instance with Cloud Function i.e. change label "status=active" to "status=tobedeleted".
Is there a way to do it with Cloud Function and node.js ?
Looks as if the method compute.instances.setLabels requires additional libraries ?
I already created Cloud Function to stop/start instances.
Here is the error :
resource: {…}
severity: "ERROR"
textPayload: "{ Error: Login Required
at Gaxios.request (/srv/node_modules/googleapis-common/node_modules/gaxios/build/src/gaxios.js:70:23)
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
response:
{ config:
{ url: 'https://www.googleapis.com/compute/v1/projects/wpress-v1/zones/us-central1-a/instances/instance-1/setLabels?labels%5Bis-scheduled%5D=manual',
method: 'POST',
paramsSerializer: [Function],
headers: [Object],
params: [Object],
validateStatus: [Function],
retry: true,
responseType: 'json',
retryConfig: [Object] },
data: { error: [Object] },
Then here is my code :
const Compute = require('#google-cloud/compute');
/*const compute = new Compute();*/
const {google} = require('googleapis');
/*const google = require('#google-cloud/googleapis');*/
var compute = google.compute('v1');
exports.setInstanceScheduleMode = (event, context, callback) => {
try {
const payload = _validatePayload(
JSON.parse(Buffer.from(event.data, 'base64').toString())
);
var request = {
project: 'wpress-v1',
zone: 'us-central1-a',
instance: 'instance-1',
labels: {
"is-scheduled": "manual"
},
auth: google.authClient,
};
compute.instances.setLabels(request, function(err, response) {
if (err) {
console.error(err);
return;
}
console.log(JSON.stringify(response, null, 2));
});
} catch (err) {
console.log(err);
callback(err);
}
};
// [END functions_start_instance_pubsub]
function _validatePayload(payload) {
if (!payload.zone) {
throw new Error(`Attribute 'zone' missing from payload`);
} else if (!payload.label) {
throw new Error(`Attribute 'label' missing from payload`);
}
else if (!payload.instance) {
throw new Error(`Attribute 'instance' missing from payload`);
}
return payload;
}
function authorize(callback) {
google.auth.getClient({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
}).then(client => {
callback(client);
}).catch(err => {
console.error('authentication failed: ', err);
});
}
There's a good number of things going on in this code. This isn't a straightforward operation, and I do wish there were a few more examples in the documentation of how to do this.
First, it appears that the #google-cloud/compute idiomatic library doesn't support a setLabels function on its VMs object, so we're forced to use the node REST library, which isn't quite as easy to use. The code you have written seems to mix the two in a somewhat confusing way, but is mostly already using the REST API so we can start from there. For reference, the setLabels REST API documentation.
Second, the authentication error you are getting is because you haven't properly intilized the authClient for the REST API, in particular by granting it the correct scope. (Notably, the authorize() method is never called, unlike in the sample code). This needs to be called to at least request the https://www.googleapis.com/auth/compute scope, though the cloud-platform scope will also work, as it is more privileged. This is what is leading to your immediate authentication error.
It is also possible that you are running the cloud function as an IAM account without the necessary roles, but both the default compute engine and default app engine accounts should be able to do this, so it appears to be that the scopes aren't requested.
Finally, even if this was working, you would find that the setLabels method requires a fingerprint of the current label values, or it would return a CONDITION_FAILURE -- essentially, when you call setLabels you are fully replacing the labels on the instance, so the API wants to make sure two callers aren't competing at once.
All together, that leads us to this (for simplicity, I used an HTTP function, but of course you can use your existing trigger just as well):
const { google } = require('googleapis');
const computeClient = google.compute('v1');
exports.labelInstance = async (req, res) => {
// First, get the auth scope we need. Thankfully cloud functions runs with
// application default credentials, so we don't need to do anything with keys, etc
// as long as the service account we are configured to run as has the right permissions.
//
// We only need the compute scope, we don't need all of cloud-platform, so limit ourselves to that.
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/compute']
});
const authClient = await auth.getClient();
// Build our request
var baseRequest = {
project: 'YOUR-PROJECT-NAME',
zone: 'us-central1-a',
instance: 'instance-1',
auth: authClient
};
// We need to get the existing labels and fingerprint first.
return computeClient.instances.get(baseRequest).then(result => {
// We need all the fields from baseRequest again, and we want to keep the old labels.
// I'm sort of cheating here, since it isn't a deep copy, but it works within the
// scope of this function.
setRequest = baseRequest;
// As setLabels is a POST request, we need to put the parameters in the requestBody.
setRequest.requestBody = {
labels: result.data.labels || {},
labelFingerprint: result.data.labelFingerprint // Needed to avoid CONDITION_FAILURE
};
// And add our new label...
setRequest.requestBody.labels['my-new-label'] = 'my-new-value';
return computeClient.instances.setLabels(setRequest);
}).then(result => {
console.log('set done');
console.log(result);
return res.send('ok');
}).catch(error => {
console.error('Error!');
console.error(error);
return res.send('error');
});
};
In your original question you wanted to change a label. Obviously you can adjust the code above to remove any labels out of the set that is retrieved with the fingerprint that you like, you don't have to copy them all.
Also be aware that the above code doesn't actually wait for the operation to complete (as operations are asynchronous -- the result that is returned will likely be in the RUNNING state), you would need to further use the REST API to check on the status of the operation. I haven't done that as it is somewhat outside the scope of this question, but you can read about it here.
Google Cloud Platform documentation provides a detailed overview of instances.setLabels method, which is a part of Google's Node.js client library.
See the Node.js code sample mentioned in GCP documentation below:
// BEFORE RUNNING:
// ---------------
// 1. If not already done, enable the Compute Engine API
// and check the quota for your project at
// https://console.developers.google.com/apis/api/compute
// 2. This sample uses Application Default Credentials for authentication.
// If not already done, install the gcloud CLI from
// https://cloud.google.com/sdk and run
// `gcloud beta auth application-default login`.
// For more information, see
// https://developers.google.com/identity/protocols/application-default-credentials
// 3. Install the Node.js client library by running
// `npm install googleapis --save`
const {google} = require('googleapis');
var compute = google.compute('v1');
authorize(function(authClient) {
var request = {
// Project ID for this request.
project: 'my-project', // TODO: Update placeholder value.
// The name of the zone for this request.
zone: 'my-zone', // TODO: Update placeholder value.
// Name of the instance scoping this request.
instance: 'my-instance', // TODO: Update placeholder value.
resource: {
// TODO: Add desired properties to the request body.
},
auth: authClient,
};
compute.instances.setLabels(request, function(err, response) {
if (err) {
console.error(err);
return;
}
// TODO: Change code below to process the `response` object:
console.log(JSON.stringify(response, null, 2));
});
});
function authorize(callback) {
google.auth.getClient({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
}).then(client => {
callback(client);
}).catch(err => {
console.error('authentication failed: ', err);
});
}
Remember to send the request body as a parameter when writing your code.
Consider the following when using this method:
You will need to specify the current labels your instance has under labelFingerprint.
Your instance's labels will be overwritten, so make sure to include in request body any labels you'd want to keep.

users.list returns 403 Error: Not Authorized to access this resource/api

I am trying to retrieve a list of users using the node.js googleapis library and a service account.
I followed this guide to 'Perform Google Apps Domain-Wide Delegation of Authority'. There are examples for Java and Python, but unfortunately not for node.js, which seems to work rather differently.
I tried following the quickstart and completed the first two steps, but then it uses a manual OAuth flow instead of a service account.
So I tried to follow the example here to authorize using a service account. That all seems to work until I send the request, then I get an error: Error: Not Authorized to access this resource/api with code: 403.
Here's my code:
var google = require('googleapis'),
GoogleAuth = require('google-auth-library'),
authFactory = new GoogleAuth(),
admin = google.admin('directory_v1')
authFactory.getApplicationDefault(function (err, authClient) {
console.log('GOT APPLICATION DEFAULT', authClient)
if (err) {
console.log('Authentication failed because of ', err);
return;
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
console.log('SCOPE REQUIRED')
var scopes = ['https://www.googleapis.com/auth/admin.directory.user'];
authClient = authClient.createScoped(scopes);
}
var request = {
auth: authClient,
domain: 'mydomain.com'
};
console.log('request:', request)
admin.users.list(request, function (err, result) {
if (err) {
console.log('admin.users.list error', err);
} else {
console.log(result);
}
});
});
What have I missed please?
After several hours of experimenting I came to the conclusion that this particular API cannot be accessed with a service account. Although it is not explicitly stated in the docs anywhere that I could find, the quickstart seems to overcome this limitation by using an OAuth process and then storing in a file the tokens required to authorize future requests. If I'm wrong please add a better answer!
My solution is to use the quickstart project to generate those tokens and then add the credentials and tokens from the quickstart to my project and use them whenever my server starts, something like:
let tokens = require('./credentials/tokens.json'),
credentials = require('./credentials/oauth_credentials.json'),
clientSecret = credentials.installed.client_secret,
clientId = credentials.installed.client_id,
redirectUrl = credentials.installed.redirect_uris[0],
google = require('googleapis'),
GoogleAuth = require('google-auth-library'),
authFactory = new GoogleAuth(),
admin = google.admin('directory_v1'),
oauth2Client = new authFactory.OAuth2(clientId, clientSecret, redirectUrl);
oauth2Client.credentials = tokens;
let request = {
auth: oauth2Client,
domain: 'coachaxis.com'
};
console.log('request:', request)
admin.users.list(request, function (err, result) {
if (err) {
console.log('admin.users.list error', err);
} else {
console.log(result);
}
});
It's not elegant but it works.

Resources