Protect sensitive data in custom elements
Custom elements give you the opportunity to expand your Kontent.ai project beyond its built-in features. You can make your custom elements reusable and configurable in your project by entering JSON parameter properties. However, data entered in this field, including sensitive data such as API keys or credentials, can be viewed by any of your contributors, including those who do not have the permission to view sensitive data.
Table of contents
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.
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.
In this tutorial you will:
- 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.

AWS Management Console
On the next screen, click Create a function.

Creating a new AWS Lambda function
To create the function:
- Select Author from scratch
- Name your function requestRepeater.
- Under Runtime, select Node.js.
- Click Create function.

Specify function name and runtime.
On the function's Configuration page:
- 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.
Note: For demonstration purposes, this endpoint will be publicly available and can be invoked by all users.

Add API Gateway.
Be sure to copy the API endpoint to your clipboard because you will need to enter it in Step 3. This URL is the endpoint of our proxy server. Click the requestRepeater Lambda function.

API Gateway URL.
Paste the following code in Function code.
This code covers a scenario using GET requests returning a JSON response. If you are using a different endpoint, adjust the code to meet your needs. For example, if API is only able to serve XML content types, the Accept header would have to be changed to headers['Accept'] = 'application/xml';
.
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}"`)); } };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}"`)); } };
Click Create function and then Save.
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

AWS Lambda environment variables
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{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
In our example:
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]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]
If entered correctly, you will receive a JSON response from the Magento server which was delivered via your new Lambda function.

JSON response
To learn more about Magento API, see more about our Magento integration.
Summarized
When you need to enter sensitive data to your custom element configuration, use an external server-side service like AWS Lambda or Azure Function. After you complete the integration with the service, you get a server-side proxy to handle your sensitive data, request and response headers, and query parameters.
You can use this approach with any custom element that works with sensitive information like API keys.