Protect sensitive data in custom elements
Custom elements enhance your Kontent.ai project by allowing additional functionality. Make them reusable by configuring JSON parameter properties. Bear in mind, though, that all contributors, regardless of their permission levels, can view data in this field, including sensitive information like API keys.
To manage our products, we will use Magento's API. Because Magento exposes your management API key for your products, we want to provide a content editor with only a read-only product selector. In short, we don't want to allow a content editor to edit, create or delete products.In addition, Magento's API requires an access token while requesting their API which is also sensitive data and should not be shown in your custom element's properties. In this tutorial, you'll learn how to use and protect your sensitive data needed for custom elements by creating a server-side proxy solution to handle your sensitive information.
In this tutorial, you will:
Click Create function and then Save.
In our example:
If entered correctly, you will receive a JSON response from the Magento server which was delivered via your new Lambda function.
To learn more about Magento API, see more about our Magento integration.
Server-side proxy
As a proxy, we will configure a simple AWS Lambda function, which will receive requests from our custom element, add an authentication token, copy headers and query strings, send the request to Magento API, and then resend the response back to our custom element. Since the authentication token is stored within the AWS Lambda function, it's not accessible to contributors on your project.- Create an AWS Lambda serverless function with API Gateway.
- Configure the proper host and authentication token.
- Send test request to our proxy.
Step 1: Creating an AWS Lambda function
First, you need an Amazon Web Services account. Visit Amazon Web Services and if you don't have an account, sign up. Once you are logged in, select the data center that best fits your location or purpose in the upper-right corner. Expand All services and click Lambda. On the next screen, click Create a function. To create the function:- Select Author from scratch
- Name your function requestRepeater.
- Under Runtime, select Node.js.
- Click Create function.
- Click API Gateway trigger from the left-side panel.
- Navigate to Configure triggers.
- Under API, select Create a new API.
- Under Security, select Open.
- Click Add in the lower right of the screen.
- Click Save in the upper-right corner.
const https = require('https');
const querystring = require('querystring');
/* ========Config Section======== */
const host = process.env.HOST;
const path = process.env.PATH;
const accessControlAllowOriginValue = process.env.ACCESS_CONTROL_ALLOW_ORIGIN;
const accessControlAllowHeadersValue = process.env.ACCESS_CONTROL_ALLOW_HEADERS;
// Bearer token authentization
const bearerToken = process.env.BEARER_TOKEN;
// Basic authentication credentials
const username = process.env.USERNAME;
const password = process.env.PASSWORD;
/* ========Config Section======== */
let authorizationHeaderValue;
if (bearerToken || (username && password)) {
authorizationHeaderValue = bearerToken ?
`Bearer ${bearerToken}` :
`Basic ${new Buffer(username + ":" + password).toString('base64')}`;
}
const request = (queryStringParameters, headers) => {
const requestOptions = {
host: host,
path: path,
port: 443,
method: 'GET',
};
if (queryStringParameters) {
requestOptions.path = `${requestOptions.path}?${querystring.stringify(queryStringParameters)}`;
}
if (authorizationHeaderValue) {
headers['Authorization'] = authorizationHeaderValue;
}
headers['Accept'] = 'application/json';
headers['accept-encoding'] = 'identity';
headers['Host'] = host;
requestOptions.headers = headers;
return new Promise((resolve, reject) => {
https.request(requestOptions, response => {
let data = '';
response.on('data', chunk => {
data += chunk;
});
response.on('end', () => {
const dataObject = JSON.parse(data);
response.data = dataObject;
resolve(response);
});
})
.on('error', error => {
reject(error);
})
.end();
});
};
exports.handler = (event, context, callback) => {
const corsHeaders = {
'Access-Control-Allow-Origin': accessControlAllowOriginValue,
'Access-Control-Allow-Headers': accessControlAllowHeadersValue
};
const repeatResponse = (response) => {
let multiValueHeaders = {};
for (const headerName in response.headers) {
if (Array.isArray(response.headers[headerName])) {
multiValueHeaders[headerName] = response.headers[headerName];
delete response.headers[headerName];
}
}
callback(null, {
statusCode: response.statusCode,
body: JSON.stringify(response.data),
headers: { ...response.headers, ...corsHeaders },
multiValueHeaders: multiValueHeaders,
});
};
const sendError = (error) => {
callback(null, {
statusCode: '400',
body: JSON.stringify(error),
headers: corsHeaders,
});
};
switch (event.httpMethod) {
case 'GET':
request(event.queryStringParameters, event.headers)
.then((response) => {
repeatResponse(response);
})
.catch(error => {
sendError(error);
});
break;
default:
sendError(new Error(`Unsupported method "${event.httpMethod}"`));
}
};
Step 2: Configuring your Lambda function
In the Environment variables section, enter the following keys and values:BEARER_TOKEN
– Your Magento access tokenHOST
–demo1-m2.mage.direct
PATH
–/index.php/rest/V1/products
Step 3: Testing the proxy
To test the Magento API via your new proxy (which is accessible through the API Gateway), enter your requestRepeater API endpoint as{Gateway API URL}
and then enter the URL in your browser address bar to try the GET request:
{Gateway API URL}?searchCriteria[pageSize]=10&searchCriteria[filterGroups][0][filters][0][field]=name&searchCriteria[filterGroups][0][filters][0][conditionType]=like&searchCriteria[filterGroups][0][filters][0][value]=%25watch%25
https://vpzvj1fspi.execute-api.eu-central-1.amazonaws.com/default/requestRepeater?searchCriteria[pageSize]=10&searchCriteria[filterGroups][0][filters][0][field]=name&searchCriteria[filterGroups][0][filters][0][conditionType]=like&searchCriteria[filterGroups][0][filters][0]