@udene/react-native-sdk
Version:
Udene Fraud Detection SDK for React Native
271 lines (270 loc) • 10.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UdeneClient = void 0;
const axios_1 = __importDefault(require("axios"));
const react_native_1 = require("react-native");
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
/**
* Main client for Udene fraud detection services
*/
class UdeneClient {
/**
* Creates a new UdeneClient instance
*
* @param config - Configuration options or API key string
*/
constructor(configOrApiKey) {
this.SDK_VERSION = '1.0.0';
this.STORAGE_KEY = '@udene/device-fingerprint';
// Handle string API key or config object
const config = typeof configOrApiKey === 'string'
? { apiKey: configOrApiKey }
: configOrApiKey;
if (!config.apiKey) {
throw new Error('API key is required');
}
// Set configuration with defaults
this.platform = config.platform || react_native_1.Platform.OS || 'react-native';
this.maxRetries = config.maxRetries || 3;
this.disableLogging = config.disableLogging || false;
this.logger = config.logger || ((message, error) => {
if (!this.disableLogging) {
console.warn(message, error);
}
});
// Create axios client with interceptors for retries and security
this.client = axios_1.default.create({
baseURL: config.baseURL || 'https://api.udene.net/v1',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
'X-Udene-SDK': `react-native-sdk/${this.SDK_VERSION}`,
'X-Udene-Platform': this.platform
}
});
// Add request interceptor for logging
this.client.interceptors.request.use((config) => {
if (!this.disableLogging) {
this.logger(`Request to ${config.url}`);
}
return config;
}, (error) => {
this.logger('Request error', error);
return Promise.reject(error);
});
// Add response interceptor for retries and logging
this.client.interceptors.response.use((response) => {
if (!this.disableLogging) {
this.logger(`Response from ${response.config.url}: ${response.status}`);
}
return response;
}, (error) => __awaiter(this, void 0, void 0, function* () {
var _a;
// Get the original request config
const originalRequest = error.config;
// If we haven't set a retry count yet, initialize it
if (originalRequest && !originalRequest._retryCount) {
originalRequest._retryCount = 0;
}
// Check if we should retry the request
if (originalRequest &&
originalRequest._retryCount < this.maxRetries &&
(((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) >= 500 || error.code === 'ECONNABORTED')) {
originalRequest._retryCount++;
// Exponential backoff delay
const delay = Math.pow(2, originalRequest._retryCount) * 100;
this.logger(`Retrying request (${originalRequest._retryCount}/${this.maxRetries}) after ${delay}ms`);
return new Promise((resolve) => {
setTimeout(() => resolve(this.client(originalRequest)), delay);
});
}
this.handleError(error);
return Promise.reject(error);
}));
}
/**
* Sanitize data by removing sensitive fields and validating inputs
*
* @param data - Data to sanitize
* @returns Sanitized data
* @private
*/
sanitizeData(data) {
// Create a deep copy to avoid modifying the original
const sanitized = JSON.parse(JSON.stringify(data));
// Remove sensitive fields that should never be sent
const sensitiveFields = ['password', 'ssn', 'creditCard', 'cvv'];
const removeSensitive = (obj) => {
if (!obj || typeof obj !== 'object')
return;
for (const key of Object.keys(obj)) {
if (sensitiveFields.includes(key.toLowerCase())) {
delete obj[key];
}
else if (typeof obj[key] === 'object') {
removeSensitive(obj[key]);
}
}
};
removeSensitive(sanitized);
return sanitized;
}
/**
* Get fraud metrics
*
* @returns Promise with metrics data
*/
getMetrics() {
return __awaiter(this, void 0, void 0, function* () {
try {
const response = yield this.client.get('/metrics');
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Analyze email for Business Email Compromise (BEC) threats
*
* @param emailData - Email data to analyze
* @returns Promise with analysis results
*/
analyzeBEC(emailData) {
return __awaiter(this, void 0, void 0, function* () {
try {
const sanitizedData = this.sanitizeData(emailData);
const response = yield this.client.post('/analyze/bec', sanitizedData);
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Track user interaction for fraud analysis
*
* @param data - Interaction data to track
* @returns Promise with tracking results
*/
trackInteraction(data) {
return __awaiter(this, void 0, void 0, function* () {
try {
const sanitizedData = this.sanitizeData(data);
const response = yield this.client.post('/track', sanitizedData);
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Get user activity data
*
* @returns Promise with activity data
*/
getActivity() {
return __awaiter(this, void 0, void 0, function* () {
try {
const response = yield this.client.get('/activity');
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Analyze a transaction for fraud detection
*
* @param transactionData - Transaction data to analyze
* @returns Promise with analysis results including recommendation
*/
analyzeTransaction(transactionData) {
return __awaiter(this, void 0, void 0, function* () {
try {
const sanitizedData = this.sanitizeData(transactionData);
const response = yield this.client.post('/analyze/transaction', sanitizedData);
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Get device fingerprint information
*
* @returns Promise with device fingerprint data
*/
getDeviceFingerprint() {
return __awaiter(this, void 0, void 0, function* () {
try {
// Try to get cached fingerprint first
const cachedFingerprint = yield async_storage_1.default.getItem(this.STORAGE_KEY);
if (cachedFingerprint) {
const fingerprintData = JSON.parse(cachedFingerprint);
const cacheAge = Date.now() - fingerprintData.timestamp;
// Use cached fingerprint if it's less than 24 hours old
if (cacheAge < 24 * 60 * 60 * 1000) {
return fingerprintData.data;
}
}
// Get fresh fingerprint from API
const response = yield this.client.get('/device/fingerprint');
// Cache the fingerprint
yield async_storage_1.default.setItem(this.STORAGE_KEY, JSON.stringify({
data: response.data,
timestamp: Date.now()
}));
return response.data;
}
catch (error) {
this.handleError(error);
throw error;
}
});
}
/**
* Handle API errors
*
* @param error - Error object from API request
* @private
*/
handleError(error) {
var _a, _b, _c, _d, _e, _f, _g;
if (axios_1.default.isAxiosError(error)) {
// Create a sanitized error object that doesn't expose sensitive details
const sanitizedError = {
status: (_a = error.response) === null || _a === void 0 ? void 0 : _a.status,
message: ((_d = (_c = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.message) || 'An error occurred',
code: ((_g = (_f = (_e = error.response) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.error) === null || _g === void 0 ? void 0 : _g.code) || 'unknown_error'
};
this.logger('API Error', sanitizedError);
}
else {
this.logger('Non-API Error', { message: error.message });
}
}
}
exports.UdeneClient = UdeneClient;