I'm trying to implement SumSub, a verification tool with my SvelteKit application. All API requests require authentication in order to generate AccessTokens which are required to initiate their WebSDK on the front end of my application.
I'm following their example (https://github.com/SumSubstance/AppTokenUsageExamples/tree/master/JS) in order to generate an app token server-side which I can then bring into the client side to init the WebSDK.
In "sumsubinit.js" on server side, I have the following code:
import axios from 'axios';
import FormData from 'form-data';
import crypto from "crypto";
let accesstoken123;
let externalUserId;
let levelName;
let response;
// These parameters should be used for all requests
const SUMSUB_APP_TOKEN = "TOKEN"; // Example: sbx:uY0CgwELmgUAEyl4hNWxLngb.0WSeQeiYny4WEqmAALEAiK2qTC96fBad - Please don't forget to change when switching to production
const SUMSUB_SECRET_KEY = "KEY"; // Example: Hej2ch71kG2kTd1iIUDZFNsO5C1lh5Gq - Please don't forget to change when switching to production
const SUMSUB_BASE_URL = 'https://api.sumsub.com';
var config = {};
config.baseURL= SUMSUB_BASE_URL;
axios.interceptors.request.use(createSignature, function (error) {
return Promise.reject(error);
})
// This function creates signature for the request as described here: https://developers.sumsub.com/api-reference/#app-tokens
function createSignature(config) {
console.log('Creating a signature for the request...');
var ts = Math.floor(Date.now() / 1000);
const signature = crypto.createHmac('sha256', SUMSUB_SECRET_KEY);
signature.update(ts + config.method.toUpperCase() + config.url);
if (config.data instanceof FormData) {
signature.update(config.data.getBuffer());
} else if (config.data) {
signature.update(config.data);
}
config.headers['Access-Control-Allow-Origin'] = "*";
config.headers['X-App-Access-Ts'] = ts;
config.headers['X-App-Access-Sig'] = signature.digest('hex');
return config;
}
// These functions configure requests for specified method
// https://developers.sumsub.com/api-reference/#creating-an-applicant
function createApplicant(externalUserId, levelName) {
console.log("Creating an applicant...");
var method = 'post';
var url = '/resources/applicants?levelName=' + levelName;
var ts = Math.floor(Date.now() / 1000);
var body = {
externalUserId: externalUserId
};
var headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = JSON.stringify(body);
return config;
}
// https://developers.sumsub.com/api-reference/#adding-an-id-document
// https://developers.sumsub.com/api-reference/#getting-applicant-status-sdk
function getApplicantStatus(applicantId) {
console.log("Getting the applicant status...");
var method = 'get';
var url = `/resources/applicants/${applicantId}/status`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// https://developers.sumsub.com/api-reference/#access-tokens-for-sdks
function createAccessToken (externalUserId, levelName = 'basic-kyc-level', ttlInSecs = 600) {
console.log("Creating an access token for initializng SDK...");
var method = 'post';
var url = `/resources/accessTokens?userId=${externalUserId}&ttlInSecs=${ttlInSecs}&levelName=${levelName}`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// This section contains requests to server using configuration functions
// The description of the flow can be found here: https://developers.sumsub.com/api-flow/#api-integration-phases
// Such actions are presented below:
// 1) Creating an applicant
// 2) Adding a document to the applicant
// 3) Getting applicant status
// 4) Getting access tokens for SDKs
export async function main() {
externalUserId = "random-JSToken-" + Math.random().toString(36).substr(2, 9);
levelName = 'basic-kyc-level';
console.log("External UserID: ", externalUserId);
response = await axios(createApplicant(externalUserId, levelName))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
const applicantId = response.data.id;
console.log("ApplicantID: ", applicantId);
response = await axios(addDocument(applicantId))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
response = await axios(getApplicantStatus(applicantId))
.then(function (response) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
response = await axios(createAccessToken(externalUserId, levelName, 1200))
.then(function (response) {
console.log("Response:\n", response.data);
accesstoken123 = response.data.token;
return response;
})
.catch(function (error) {
// console.log("Error:\n", error.response.data);
});
return accesstoken123.toString();
console.log(accesstoken123);
}
main();
Which is just ripped straight from the GitHub tutorial. On my front end, I have an import of the "main" function and also run it in-app. On Server Side, it runs absolutely fine and generates the necessary token. However, in-browser it fails every single time.
I feel this might be incredibly stupid, or naive but I've been at this for several days now and I'm pulling my hair out and feel that it may be something to do with CORS, my limited understanding or even just the way I've tried to configure this for Svelte, but genuinely ANY ANY ANY help, guidance or direction at this point would be incredibly appreciated and I would be super super grateful for anything.