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.
Related
The auth method used for the vault in my company's organization is via guthub token. This authentication method has already been used by some of the scala projects in the company. They are successfully able to use the authentication method to read the secrets.
This is the scala piece of code
val vaultConfig = new VaultConfig().address(VaultAddress).build()
val apiToken = new Auth(vaultConfig).loginByGithub(githubToken).getAuthClientToken
val configWithToken: VaultConfig = new VaultConfig().address(VaultAddress).token(apiToken).build()
new Vault(configWithToken)
Now we are integrating vault in one of the new NodeJS projects. So far I have written this piece of code by using the library node-vault
const vault = require("node-vault")({
apiVersion: "v1",
endpoint: "vaultURL",
});
const GITHUB_TOKEN = '';
const run = async () => {
try {
const result = await vault.githubLogin({ token: GITHUB_TOKEN });
vault.token = result.auth.client_token;
console.log('Client Token', vault.token);
const { data : returnValue } = await vault.read("some path");
const { data } = returnValue;
const { keys } = data;
console.log("myKeys", keys);
} catch (error) {
console.log(error.message);
}
};
run();
The authentication works perfectly, I'm getting the client token. But Im not able to read the secrets in the path that I give.
Note Im able to see the secrets of the same path through UI. but the code gives permission denied error.
What is it that I'm missing?
Please note that Im a beginner to both node js and hashicorp vault. The documentation of node-vault is not helping.
Any help would be appreciated. Any helpful reading material or tutorial.
On searching over the internet I found that It might have something to do with the vault policy settings. So, I was finally able to get this thing to work, I had to append data in the path for a successful read from the vault. Because data was in-fact appended with the path when I looked into the organisation's policy document.
const vault = require("node-vault")({
apiVersion: "v1",
endpoint: "vaultURL",
});
const GITHUB_TOKEN = '';
const run = async () => {
try {
const result = await vault.githubLogin({ token: GITHUB_TOKEN });
vault.token = result.auth.client_token;
console.log('Client Token', vault.token);
const { data : { data } } = await vault.read("root/data/path"); // <---- important
console.log('data', data);
} catch (error) {
console.log(error.message);
}
};
run();
My original path was
secret/apiKey
Had to use
secret/data/apiKey
For reference take a look at this answer on github.
I followed the Microsoft tutorial on this link: https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-node but I want to have them as separate functions instead of putting them into one main function.
This is what I have, setSecret works fine but getSecret is returning Promise ?
Both getSecret and setSecret return a Promise because they are asynchronous methods that need to make an HTTP request to the Key Vault service.
If you were to try the following:
const secretPromise = client.setSecret(secretName, secretValue);
You'll notice that secretPromise is a Promise<KeyVaultSecret> as per the API documentation
This allows you to wait for the secret to be set and get back the newly set secret:
const secret = await client.setSecret(secretName, secretValue);
Be mindful that by not waiting for the setSecret call to succeed you will be unable to:
Get the newly created secret (unless you get lucky with timing)
Be notified and handle any errors if setSecret fails (you can verify this by creating the secret client with an invalid Key Vault URL and running both functions - azureSetSecret will claim success but azureGetSecret will throw an error)
I'm not sure if you are trying to find a way which allows you to obtain key vault secrets in nodejs. As the quick start sample you mentioned above, microsoft provides a async method await client.getSecret(secretName) for nodejs.
Here I'd like to recommend you using rest api to access key vault secret. When calling the api, you need to generate an access token as the request header, you can refer to this sample or take a look at my test project below.
calling api:
const axios = require('axios');
const auth = require('./credenticalflow');
let vaultName = 'key vault name';
let keyName = 'key name';
let accesstoken = '';
let secret = '';
init();
async function init(){
const authResponse = await auth.getToken(auth.tokenRequest);
accesstoken = authResponse.accessToken;
getsecret(vaultName,keyName,accesstoken);
console.log("22222222:"+secret);
}
function getsecret(vaultName,keyName,token){
console.log('the token is :'+ token);
axios.get('https://'+vaultName+'.vault.azure.net/secrets/'+keyName+'/7d4b682f5c9a41578602aa5b86611aa7?api-version=7.1',{
headers: {
'Authorization': 'Bearer '+token
}
})
.then(function (response) {
// handle success
secret = response.data.value;
console.log("1111111:"+secret);
})
.catch(function (error) {
// handle error
console.log('error');
});
}
generate access token:
const msal = require('#azure/msal-node');
const msalConfig = {
auth: {
clientId: 'azure ad app cilent id',
authority: 'https://login.microsoftonline.com/<your tenant name such as xx.onmicrosoft.com>',
clientSecret: 'client secret for the azure ad app',
}
};
const tokenRequest = {
scopes: ['https://vault.azure.net/.default'],
};
const cca = new msal.ConfidentialClientApplication(msalConfig);
async function getToken(tokenRequest) {
return await cca.acquireTokenByClientCredential(tokenRequest);
}
module.exports = {
tokenRequest: tokenRequest,
getToken: getToken
};
i try to set connection to google sheets api with this code:
const { google } = require("googleapis");
const keys = require("./keys.json");
const client = google.auth.JWT(keys.client_email, null, keys.private_key, [
"https://www.googleapis.com/auth/spreadsheets",
]);
client.authorized(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("connected");
}
});
i get this error when run on vscode:
TypeError: Class constructor JWT cannot be invoked without 'new'
thank you
Modification points:
In this case, please add new like new google.auth.JWT(). This is from your error message.
client has not method of authorized(). Please modify to authorize().
When above points are reflected to your script, it becomes as follows.
Modified formula:
const { google } = require("googleapis");
const keys = require("./keys.json");
const client = new google.auth.JWT(keys.client_email, null, keys.private_key, [
"https://www.googleapis.com/auth/spreadsheets",
]);
client.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("connected");
}
});
const sheets = google.sheets({ version: "v4", auth: client }); // You can use the methods of Sheets API by this "sheets".
Note:
In this modification, it supposes that your keys.json is the file including the valid credential values of the service account. Please be careful this.
As the additional information, when you want to access to the Google Spreadsheet of your Google Drive, please share your Google Spreadsheet with the email of the service account. By this, you can access to it.
I have VM up and running in Azure. I wanted to call Azure VM from Node. I am totally unware about the get API for Azure. So how can this be achieved?
I have tenantID, ClientID, Client Secret and subscription id.
Also is there any thing unique I can get from running VM which will be same if I create another VM on Azure?
Try this considering you have the values in to access vault in environment variable to get secrets from azure vault:
var _ = require('lodash'),
msRestAzure = require('ms-rest-azure'),
KeyVault = require('azure-keyvault'),
AuthenticationContext = require('adal-node').AuthenticationContext,//Utility function to get key vault client
function getKeyVaultClient() {
// service principal details to access the vault
var clientId = process.env['CLIENT_ID']; // service principal
var domain = process.env['DOMAIN']; // tenant id
var secret = process.env['APPLICATION_SECRET'];
return new Promise(function (resolve, reject) {
msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain, function (err) {
if (err) {
return reject(err);
}
// authenticate with key vault with a service principal
var kvCredentials = new KeyVault.KeyVaultCredentials(authenticator);
keyVaultClient = new KeyVault.KeyVaultClient(kvCredentials);
return resolve(keyVaultClient);
});
})
}
function authenticator(challenge, callback) {
// service principal details to access the vault
var clientId = process.env['CLIENT_ID']; // service principal
var secret = process.env['APPLICATION_SECRET'];
// Create a new authentication context.
var context = new AuthenticationContext(challenge.authorization);
// Use the context to acquire an authentication token.
return context.acquireTokenWithClientCredentials(challenge.resource, clientId, secret, function (err, tokenResponse) {
if (err) {
return callback(err);
}
// Calculate the value to be set in the request's Authorization header and resume the call.
var authorizationValue = tokenResponse.tokenType + ' ' + tokenResponse.accessToken;
return callback(null, authorizationValue);
});
}
var vaultUri = 'https://' + process.env['VAULT_NAME'] + '.vault.azure.net/secrets/' + key;
getKeyVaultClient().then(function (vaultClient) {
vaultClient.getSecret(vaultUri,
function (err, result) {
if (err) {
return reject(err);
} else {
return resolve(result.value);
}
});
}).catch(function (err) {
reject(err);
})
You can have a look at the official documentation and examples on how to access keys and secrets using the Rest API's exposed by azure.
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.