I am trying to create a nodeJs app that gets an encrypted string from the AWS parameter store and uses it as a password for basic access authentication (username and password based authentication). For some reason though (probably asynchronous behaviour of nodeJs), I'm unable to assign the values correctly to the global variable as can be seen in the below code and stakc trace :
'use strict';
const AWS = require('aws-sdk');
const ssm = new AWS.SSM({region: 'us-east-1'});
var authPass = 'default';
let authUser = 'user';
exports.handler = (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
getParameterFromSystemManager(
(data) => {
authPass = data;
console.log('Inside func authPass : ', authPass); //This is where the printed value is 'password2' i.e. actual expected string
}
);
console.log("Outside function call : ", authPass); //This is where the printed value is 'default' when it should be password2
// Construct the Basic Auth string
const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');
// Require Basic authentication
if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
callback(null, response);
}
callback(null, request);
};
function getParameterFromSystemManager(callback) {
var params = {
Name: '/Path/ToPassword/EncryptedString',
WithDecryption: true
};
ssm.getParameter(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
callback(data.Parameter.Value);
}
});
}
As it can be seen above that the main authentication function has 2 console logs. When I test the lambda function this is the output that I receive :
Function Logs
START RequestId: 7d2eaccf-f064-4a5b-94c2-f8ff666dxxxx Version: $LATEST
2023-05-18T00:21:13.937Z 7d2eaccf-f064-4a5b-94c2-f8ff666dxxxx INFO Outside function call : default
2023-05-18T00:21:14.177Z 7d2eaccf-f064-4a5b-94c2-f8ff666dxxxx INFO Inside func : data password2 authPass password2
END RequestId: 7d2eaccf-f064-4a5b-94c2-f8ff666dxxx
REPORT RequestId: 7d2eaccf-f064-4a5b-94c2-f8ff666dxxxx Duration: 676.17 ms Billed Duration: 677 ms Memory Size: 128 MB Max Memory Used: 81 MB Init Duration: 503.74 ms
I did a little digging and found that the following minor changes make the code work. Posting this here just in case somebody else has a similar issue in the future :
'use strict';
const AWS = require('aws-sdk');
const ssm = new AWS.SSM({region: 'us-east-1'});
let authPass;
let authUser = 'user';
exports.handler = async (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
authPass = await getParam('/Path/ToPassword/EncryptedString');
console.log("authUser : ", authUser);
// Construct the Basic Auth string
const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');
// Require Basic authentication
if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
callback(null, response);
}
callback(null, request);
};
const getParam = param => {
return new Promise((res, rej) => {
ssm.getParameter({
Name : param,
WithDecryption: true
}, (err, data) => {
if (err) {
return rej(err)
}
return res(data.Parameter.Value)
})
})
}