I am working on protecting a static website with a username and password. I created a basic HTTP Authentication for CloudFront with Lambda@Edge in NodeJS.
I am completely new to NodeJS. Initially, I had the user and the password hardcoded, and this worked properly.
'use strict';
exports.handler = (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
// Configure authentication
const authUser = 'user';
const authPass = 'pass';
// 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);
}
// Continue request processing if authentication passed
callback(null, request);
};
I stored my secrets in SSM and I want to retrieve them through the function. I tested this piece of code separately in Lambda and it returns the credentials as espected.
'use strict';
exports.handler = async (event, context, callback) => {
const ssm = new (require('aws-sdk/clients/ssm'))();
let userData = await ssm.getParameters({Names: ['website-user']}).promise();
let userPass = await ssm.getParameters({Names: ['website-pass']}).promise();
let user = userData.Parameters[0].Value;
let pass = userPass.Parameters[0].Value;
return {user, pass};
};
But when I stitch the two, I get 503 ERROR The request could not be satisfied. Does anyone know what I might be doing wrong? Thank you for your help!
The complete code:
'use strict';
exports.handler = async (event, context, callback) => {
const ssm = new (require('aws-sdk/clients/ssm'))();
let userData = await ssm.getParameters({Names: ['website-user']}).promise();
let userPass = await ssm.getParameters({Names: ['website-pass']}).promise();
let user = userData.Parameters[0].Value;
let pass = userPass.Parameters[0].Value;
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
// Construct the Basic Auth string
let authString = 'Basic ' + new Buffer(user + ':' + pass).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);
}
// Continue request processing if authentication passed
callback(null, request);
};
After reading about promises I was able to resolve the error. Here's the solution that worked for me:
'use strict';
var AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });
var ssm = new AWS.SSM();
function getParameter(param) {
return new Promise(function (success, reject) {
ssm.getParameter(param, function (err, data) {
if (err) {
reject(err);
} else {
success(data);
}
});
});
};
exports.handler = (event, context, callback) => {
let request = event.Records[0].cf.request;
let headers = request.headers;
let user = {Name: 'user-path', WithDecryption: false};
let pass = {Name: 'password-path', WithDecryption: false};
let authUser;
let authPass;
var promises = [];
promises.push(getParameter(user), getParameter(pass));
Promise.all(promises)
.then(function (result) {
authUser = result[0].Parameter.Value;
authPass = result[1].Parameter.Value;
console.log(authUser);
console.log(authPass);
let authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');
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);
}
// Continue request processing if authentication passed
callback(null, request);
})
.catch(function (err) {
console.log(err);
});
};