Unfortunately due to the nature of how browsers and servers are designed, attempting to make an API request directly from client-side JavaScript to a third-party server will always fail due to browser-enforced CORS policies. These policies are in place to secure web applications by preventing malicious websites from reading sensitive data from another domain without permission.
When a web page or app tries to fetch data from a different domain (origin) using JavaScript, the JavaScript function will fail. The browser blocks the request and you’ll see a CORS error in the console.
There is a workaround for this called Server-Side Proxies, but it requires some set up on the end user’s part.
Using A Server-Side Proxy #
The most common workaround is to create a server-side proxy on the same server the end user is sending the key check from. This proxy acts as a middle man between your app’s JS code and our API server, bypassing any browser CORS policies.
Your JavaScript code to check a key will make the request to a script or endpoint on the end user’s server first, called a server-side proxy. This proxy then makes the server-to-server request to our API. Your server-side proxy will then receive the response and pass it back to your client-side JS key checking code where it can be processed normally.
This server-side request is not subject to CORS, so it can retrieve the data and then pass it back to the original client-side request without issue.
However this requires some additional set up on the end user’s part. You should inform them of the requirements and provide detailed instruction on how to do so in your app’s installation documentation.
Is Your End User Capable? #
While your app can provide the code and ability to create, interact, and run a server-side proxy, the end user must do some of the technical work involved in setting up a server-side proxy on their server. For an inexperienced end user this may seem daunting.
Given that, here are some issues you must consider first:
- This requires Node.js or similar to be installed on the end user’s server.
- The below code must be installed on the same domain the end user is sending the key check from
- The end user must have a working SSL certificate installed on the server, and set their SSL key and certificate paths in the code
- The end user must start the Node.js server we are creating below using command prompts
- The end user may need to troubleshoot the connection by trying various connection ports in the code, or even assigning ports on the server
Points 2 and 3 are fairly easy to deal with. Simply including the necessary files in your app should work in most cases, and having the end user set their key and certificate paths could be done via a UI form or by instructing them to set it in the code itself (we have made comments where this should be done).
Points 1, 4, and 5 can be tricky unless your end user is also a developer or understands servers.
For point 1, if the server your app will run on does not have Node.js or similar installed (like most shared hosting) you will not be able to perform the key check using client-side JavaScript because it cannot talk to the third-party server due to CORS policies. You will have to use another programming language or communicate this limitation to the end user.
For Point 4, the command prompt it simple but it can feel like voodoo to an unexperienced end user. The necessary command prompt are in fact very simple and will look something like this:
cd /path/to/your/project/directory
node proxyserver.js
That assumes you’ve put the below code into a file named proxyserver.js. You can name this file anything you wish.
Point 5 is also fairly simple in most cases. Using the port 443 pre-set in the code will work in most cases, however this can vary depending on the end user’s server configuration and network settings. You will need to communicate this to your end user. Ports can easily be changed in the code below or assigned on a server and network level (if the end user has access to these controls).
In short, it’s very easy to set up a server-side proxy to process your client-side JavaScript that connects to our API if the end user’s server already has Node.js or similar installed and a working SSL certificate. But an inexperienced end user, or an end user without Node.js or a SSL cert, may find the task impossible.
Either way, be sure to inform your end user ahead of time and guide them through these extra installation steps with documentation or customer support.
Sample SSP Code: #
const https = require('https');
const fs = require('fs');
// Load your actual SSL certificate and key for HTTPS
const options = {
key: fs.readFileSync('/path/to/your/ssl/key.pem'), // Path to your SSL key
cert: fs.readFileSync('/path/to/your/ssl/certificate.pem') // Path to your SSL certificate
};
// This function makes an HTTPS GET request to the external API with the license key in the request headers.
function fetchLicenseKeyStatus(licenseKey, domain, callback) {
const options = {
hostname: 'batchkeys.com',
path: '/api.php',
method: 'GET',
headers: {
'auth': authKey,
'key': licenseKey,
'domain': domain
}
};
const req = https.request(options, (res) => {
let data = '';
// A chunk of data has been received.
res.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received.
res.on('end', () => {
callback(null, data);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
callback(err);
});
req.end();
}
// Create an HTTPS server that listens for requests using the loaded SSL certificate and key.
const server = https.createServer(options, (req, res) => {
if (req.url.startsWith('/check-license')) {
const urlParams = new URLSearchParams(req.url.split('?')[1]);
const licenseKey = urlParams.get('key');
const domain = req.headers.host; // Get the domain from the request headers
if (!licenseKey) {
res.writeHead(400);
res.end('License key is required');
return;
}
// Use the fetchLicenseKeyStatus function to query the external API.
fetchLicenseKeyStatus(licenseKey, domain, (err, apiResponse) => {
if (err) {
res.writeHead(500);
res.end('Server error');
return;
}
// Forward the external API's response to the client.
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(apiResponse);
});
} else {
// Handle any other URL
res.writeHead(404);
res.end('Not Found');
}
});
// Specify the port you want your HTTPS server to listen on.
// Common HTTPS ports include 443, 8443, etc. Make sure the port you choose does not conflict with other services.
const port = 443; // or 443, 8443, etc., depending on your setup
server.listen(port, () => {
console.log(`Server running on https://localhost:${port}`);
});
Adjust this sample code to your needs and then put the code in a file called proxyserver.js and package it along with the rest of your app files. Instruct your end user on how to adjust the necessary settings (SSL and ports) and on how to start the server. As long as the proxy server is running your client-side JS files can communicate with our API without issue.
This code does the following:
- Assigns the data: It assigns the auth key, license key, and domain, and puts them in the request headers.
- Sends a GET Request to the API: It uses the HTTPS module to make a GET request to our API with the encoded license key.
- Processes the JSON Response: It collects the JSON response.
- Shows the Response to the Client: It sends the JSON response back to the client (meaning, your key checking script).
How it handles the response:
- The response from our API is directly forwarded as-is, without parsing or modifying the JSON data.
- The client script (your JS key checking script) that made the request will receive this JSON data as the response body.
- Since the Content-Type is set to application/json, the client will treat the response body as JSON and parse it accordingly.
Further Modifications Needed #
Please note that the provided code is an example and will not work as is. Very important functions such as user inputs for changing variables in a UI/form, SQL injection protections, validation, API auth key inclusion from a .env file, thorough error handing, logging, etc. are not in the above code. Nor does it include the logic to provide a key to the assigned variables, such as via a form entry or a local file.