React Native Integration
Intro
The SDK can be integrated into a React Native project.
Integration with C++ (iOS)
If your project is built with C++ (AppDelegate.mm
), you should do the following:
- Open Xcode Build Settings.
- Navigate to Targets -> Build Settings ->
Apple Clang - Custom Compiler Flags
->Other C++ Flags
. - Add the
-fcxx-modules
flag.
Add the Human Module
Android
- Create a native module called
HumanModule
.
Java / HumanModule.java:
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.humansecurity.mobile_sdk.HumanSecurity;
import com.humansecurity.mobile_sdk.main.HSBotDefenderChallengeResult;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Objects;
public class HumanModule extends ReactContextBaseJavaModule {
static HumanModule shared = null;
String strNewHeaders = "HumanNewHeaders";
String strChallengeResult = "HumanChallengeResult";
String strSolved = "solved";
String strCancelled = "cancelled";
String strFalse = "false";
HumanModule(ReactApplicationContext context) {
super(context);
}
@NonNull
@Override
public String getName() {
return "HumanModule";
}
public void handleUpdatedHeaders(HashMap<String, String> headers) {
if (!this.getReactApplicationContext().hasCatalystInstance()) {
return;
}
JSONObject json = new JSONObject(headers);
this.getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(strNewHeaders, json.toString());
}
public void handleChallengeSolvedEvent() {
if (!this.getReactApplicationContext().hasCatalystInstance()) {
return;
}
this.getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(strChallengeResult, strSolved);
}
public void handleChallengeCancelledEvent() {
if (!this.getReactApplicationContext().hasCatalystInstance()) {
return;
}
this.getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(strChallengeResult, strCancelled);
}
@ReactMethod
public void getHTTPHeaders(Promise promise) {
JSONObject json = new JSONObject(Objects.requireNonNull(HumanSecurity.INSTANCE.getBD().headersForURLRequest(null)));
promise.resolve(json.toString());
}
@ReactMethod
public void handleResponse(String response, Integer code, String url, Promise promise) {
boolean handled = HumanSecurity.INSTANCE.getBD().handleResponse(response, result -> {
promise.resolve(result == HSBotDefenderChallengeResult.SOLVED ? strSolved : strCancelled);
return null;
});
if (!handled) {
promise.resolve(strFalse);
}
}
}
- Add the
HumanModule
to theHumanPackage
and set theshared
property.
Java / HumanPackage.java:
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HumanPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
HumanModule module = new HumanModule(reactContext);
HumanModule.shared = module;
modules.add(module);
return modules;
}
}
iOS
Create a native module called HumanModule
.
Objective-C / HumanModule.h:
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
NS_ASSUME_NONNULL_BEGIN
@interface HumanModule : RCTEventEmitter <RCTBridgeModule>
+ (HumanModule *)shared;
- (void)handleUpdatedHeaders:(NSDictionary<NSString *,NSString *> *)headers;
- (void)handleChallengeSolvedEvent;
- (void)handleChallengeCancelledEvent;
@end
NS_ASSUME_NONNULL_END
Objective-C / HumanModule.m:
#import "HumanModule.h"
@import HUMAN;
static NSString *strNewHeaders = @"HumanNewHeaders";
static NSString *strChallengeResult = @"HumanChallengeResult";
static NSString *strSolved = @"solved";
static NSString *strCancelled = @"cancelled";
static NSString *strFalse = @"false";
@implementation HumanModule
static HumanModule *shared = nil;
+ (HumanModule *)shared {
return shared;
}
- (instancetype)init {
self = [super init];
shared = self;
return self;
}
- (NSArray<NSString *> *)supportedEvents {
return @[strNewHeaders, strChallengeResult];
}
- (void)handleUpdatedHeaders:(NSDictionary<NSString *,NSString *> *)headers {
NSData *data = [NSJSONSerialization dataWithJSONObject:headers options:0 error:nil];
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self sendEventWithName:strNewHeaders body:json];
}
- (void)handleChallengeSolvedEvent {
[self sendEventWithName:strChallengeResult body:strSolved];
}
- (void)handleChallengeCancelledEvent {
[self sendEventWithName:strChallengeResult body:strCancelled];
}
RCT_EXPORT_MODULE(HumanModule);
RCT_REMAP_METHOD(getHTTPHeaders,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSDictionary<NSString *, NSString *> *humanHttpHeaders = [HumanSecurity.BD headersForURLRequestForAppId:nil];
NSData *data = [NSJSONSerialization dataWithJSONObject:humanHttpHeaders options:0 error:nil];
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
resolve(@[json]);
}
RCT_REMAP_METHOD(handleResponse,
response:(NSString *)response
code:(NSInteger)code
url:(NSString *)url
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
NSHTTPURLResponse *httpURLResponse = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:url] statusCode:code HTTPVersion:nil headerFields:nil];
BOOL handled = [HumanSecurity.BD handleResponseWithResponse:httpURLResponse data:data callback:^(enum HSBotDefenderChallengeResult result) {
resolve((result == HSBotDefenderChallengeResultSolved ? strSolved : strCancelled));
}];
if (!handled) {
resolve(strFalse);
}
}
@end
How to start the SDK
- The most important thing is to start the SDK as soon as possible in your app flow. The reason for that is when your app sends a URL request to your server before the SDK was started, the request will not include the SDK's HTTP headers. As a result, HUMAN's Enforcer could block the request, and the SDK will not be able to present a challenge to the user. The best place to start the SDK is in the:
Application
'sonCreate
function on Android.AppDelegate
'sdidFinishLaunchingWithOptions
function on iOS.
- You should start the SDK on the main thread.
- Implement the
HumanDelegate
to receive events from the SDK.
Here is an example of how it should be:
Android
Java / MainApplication.java:
import android.app.Application;
import android.content.Context;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
import com.human_demo.newarchitecture.MainApplicationReactNativeHost;
import com.humansecurity.mobile_sdk.HumanSecurity;
import com.humansecurity.mobile_sdk.main.HSBotDefenderDelegate;
import com.humansecurity.mobile_sdk.main.policy.HSAutomaticInterceptorType;
import com.humansecurity.mobile_sdk.main.policy.HSPolicy;
public class MainApplication extends Application implements ReactApplication, HSBotDefenderDelegate {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new HumanPackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
}
@Override
public void onCreate() {
super.onCreate();
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
startHumanSDK();
}
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
Class<?> aClass = Class.forName("<package_name>.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void startHumanSDK() {
HSPolicy policy = new HSPolicy();
policy.getAutomaticInterceptorPolicy().setInterceptorType(HSAutomaticInterceptorType.NONE);
try {
HumanSecurity.INSTANCE.start(this, "<APP_ID>", policy);
HumanSecurity.INSTANCE.getBD().setDelegate(this);
}
catch (Exception exception) {
Log.e("MainApplication","Exception: " + exception.getMessage());
}
}
@Override
public void botDefenderDidUpdateHeaders(@NonNull HashMap<String, String> headers, @NonNull String appId) {
if (HumanModule.shared != null) {
HumanModule.shared.handleUpdatedHeaders(headers);
}
}
@Override
public void botDefenderRequestBlocked(@Nullable String url, @NonNull String appId) {
}
@Override
public void botDefenderChallengeSolved(@NonNull String appId) {
HumanModule.shared.handleChallengeSolvedEvent();
}
@Override
public void botDefenderChallengeCancelled(@NonNull String appId) {
HumanModule.shared.handleChallengeCancelledEvent();
}
@Override
public void botDefenderChallengeRendered(@NonNull String appId) {
}
@Override
public void botDefenderChallengeRenderFailed(@NonNull String appId) {
}
}
iOS
Objective-C / AppDelegate.h:
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
@import HUMAN;
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, HSBotDefenderDelegate>
@property (nonatomic, strong) UIWindow *window;
@end
Objective-C / AppDelegate.m:
#import "AppDelegate.h"
#import "HumanModule.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
HSPolicy *policy = [[HSPolicy alloc] init];
policy.automaticInterceptorPolicy.interceptorType = HSAutomaticInterceptorTypeNone;
NSError *error = nil;
[HumanSecurity startWithAppId:@"<APP_ID>" policy:policy error:&error];
HumanSecurity.BD.delegate = self;
if (error != nil) {
NSLog(@"error: %@", error);
}
[[HumanModule shared] startObserving];
// Configure React Native...
return YES;
}
// MARK: - HSBotDefenderDelegate
- (void)botDefenderRequestBlockedWithUrl:(NSURL *)url appId:(NSString *)appId {
}
- (void)botDefenderChallengeSolvedForAppId:(NSString *)appId {
[[HumanModule shared] handleChallengeSolvedEvent];
}
- (void)botDefenderChallengeCancelledForAppId:(NSString *)appId {
[[HumanModule shared] handleChallengeCancelledEvent];
}
- (void)botDefenderDidUpdateHeadersWithHeaders:(NSDictionary<NSString *,NSString *> *)headers forAppId:(NSString *)appId {
[[HumanModule shared] handleUpdatedHeaders:headers];
}
- (void)botDefenderChallengeRenderedForAppId:(NSString *)appId {
}
- (void)botDefenderChallengeRenderFailedForAppId:(NSString *)appId {
}
@end
Don't forget to change the <APP_ID>
to your own AppID.
Let's talk about what we have in the code here:
- We start the SDK as soon as possible and on the main thread.
- We create a
HSPolicy
instance to configure the SDK's behavior. Here, we set theinterceptorType
property toHSAutomaticInterceptorType.NONE
. This disables the automatic interception feature, which is not supported in React Native. - We call the
HumanSecurity.start(appId, policy)
function of the SDK with:- The
Application
instance (Android only). - Your AppID.
- The policy object.
- The
- On iOS, we call the
startObserving
function on theHumanModule
.
Note: If your app communicates with multiple servers with different AppIDs, you can call the HumanSecurity.start(appIds, policy)
function to pass an array of AppIDs. Specify the relevant AppID for each API call in the SDK.
How to add HTTP headers and handle block response
In your JavaScript code, you should:
- Import the
HumanModule
. - Create a new native event emitter.
- Add a listener to the "New Headers" event.
- Add a listener to the "Challenge Result" event.
- Add the SDK's HTTP headers to your URL requests.
- When a request is blocked, send the block response to the SDK.
- Handle the challenge's result.
JavaScript:
/* Import HumanModule */
import { NativeModules, NativeEventEmitter } from 'react-native';
const { HumanModule } = NativeModules;
/* Create a new native event emitter */
const humanEventEmitter = new NativeEventEmitter(HumanModule);
/* Add listener to the "New Headers" event and store the SDK's HTTP headers in a variable */
let humanHttpHeaders = null;
const onNewHeaders = headers => {
const obj = JSON.parse(headers);
console.log(`Got new HTTP headers: ${JSON.stringify(obj)}`);
humanHttpHeaders = obj;
};
const subscriptionHumanNewHeaders = humanEventEmitter.addListener('HumanNewHeaders', onNewHeaders);
/* Add listener to the "Challenge Result" event */
const onChallengeResult = result => {
if (result === 'solved') {
console.log('Challenge solved');
} else if (result === 'cancelled') {
console.log('Challenge cancelled');
}
};
const subscriptionHumanChallengeResult = humanEventEmitter.addListener('HumanChallengeResult', onChallengeResult);
/* Get Human's HTTP headers */
const getHumanHeaders = async () => {
if (humanHttpHeaders != null) {
return humanHttpHeaders;
}
const headers = await HumanModule.getHTTPHeaders();
const obj = JSON.parse(headers);
console.log(`[HUMAN] Got HTTP headers: ${JSON.stringify(obj)}`);
return obj;
};
/* Send URL request */
async function sendRequest(url) {
/* Get Human's HTTP headers */
const humanHeaders = await getHumanHeaders();
try {
const response = await fetch(url, {
method: 'GET',
headers: humanHeaders,
});
const json = await response.json();
/* Send the response to the SDK */
const result = await HumanModule.handleResponse(JSON.stringify(json));
if (result === 'solved') {
console.log('Challenge solved');
await sendRequest(url);
} else if (result === 'cancelled') {
console.log('Challenge cancelled');
} else if (result === 'false') {
console.log('Request was not blocked by Human');
}
} catch (error) {
console.error(error);
}
}
Understanding the block response
-
The HUMAN's Enforcer, when it decides to block a request, returns a JSON string in the response's body with an HTTP status code of 403. Here is an example of the response:
{ "vid": "928d7ab3-9cf1-11ee-a624-b802520f369f", "uuid": "fd01e6d6-9cf2-11ee-808c-acde48001122", "page": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEiPgogICAgPHRpdGxlPkFjY2VzcyB0byB0aGlzIHBhZ2UgaGFzIGJlZW4gZGVuaWVkLjwvdGl0bGU+CiAgICA8bGluayBocmVmPSJodHRwczovL2ZvbnRzLmdvb2dsZWFwaXMuY29tL2Nzcz9mYW1pbHk9T3BlbitTYW5zOjMwMCIgcmVsPSJzdHlsZXNoZWV0Ij4KICAgIDxzdHlsZT4KICAgICAgICBodG1sLCBib2R5IHsKICAgICAgICAgICAgbWFyZ2luOiAwOwogICAgICAgICAgICBwYWRkaW5nOiAwOwogICAgICAgICAgICBmb250LWZhbWlseTogJ09wZW4gU2FucycsIHNhbnMtc2VyaWY7CiAgICAgICAgICAgIGNvbG9yOiAjMDAwOwogICAgICAgIH0KCiAgICAgICAgYSB7CiAgICAgICAgICAgIGNvbG9yOiAjYzVjNWM1OwogICAgICAgICAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7CiAgICAgICAgfQoKICAgICAgICAuY29udGFpbmVyIHsKICAgICAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgICAgICAgZGlzcGxheTogZmxleDsKICAgICAgICAgICAgZmxleDogMTsKICAgICAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogICAgICAgICAgICBoZWlnaHQ6IDEwMCU7CiAgICAgICAgfQoKICAgICAgICAuY29udGFpbmVyID4gZGl2IHsKICAgICAgICAgICAgd2lkdGg6IDEwMCU7CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIGp1c3RpZnktY29udGVudDogY2VudGVyOwogICAgICAgIH0KCiAgICAgICAgLmNvbnRhaW5lciA+IGRpdiA+IGRpdiB7CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIHdpZHRoOiA4MCU7CiAgICAgICAgfQoKICAgICAgICAuY3VzdG9tZXItbG9nby13cmFwcGVyIHsKICAgICAgICAgICAgcGFkZGluZy10b3A6IDJyZW07CiAgICAgICAgICAgIGZsZXgtZ3JvdzogMDsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsKICAgICAgICAgICAgdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgIH0KCiAgICAgICAgLmN1c3RvbWVyLWxvZ28gewogICAgICAgICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgIzAwMDsKICAgICAgICB9CgogICAgICAgIC5jdXN0b21lci1sb2dvID4gaW1nIHsKICAgICAgICAgICAgcGFkZGluZy1ib3R0b206IDFyZW07CiAgICAgICAgICAgIG1heC1oZWlnaHQ6IDUwcHg7CiAgICAgICAgICAgIG1heC13aWR0aDogMTAwJTsKICAgICAgICB9CgogICAgICAgIC5wYWdlLXRpdGxlLXdyYXBwZXIgewogICAgICAgICAgICBmbGV4LWdyb3c6IDI7CiAgICAgICAgfQoKICAgICAgICAucGFnZS10aXRsZSB7CiAgICAgICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW4tcmV2ZXJzZTsKICAgICAgICB9CgogICAgICAgIC5jb250ZW50LXdyYXBwZXIgewogICAgICAgICAgICBmbGV4LWdyb3c6IDU7CiAgICAgICAgfQoKICAgICAgICAuY29udGVudCB7CiAgICAgICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICAgICAgfQoKICAgICAgICAucGFnZS1mb290ZXItd3JhcHBlciB7CiAgICAgICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgICAgICAgIGZsZXgtZ3JvdzogMC4yOwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogICAgICAgICAgICBjb2xvcjogI2M1YzVjNTsKICAgICAgICAgICAgZm9udC1zaXplOiA3MCU7CiAgICAgICAgfQoKICAgICAgICBAbWVkaWEgKG1pbi13aWR0aDogNzY4cHgpIHsKICAgICAgICAgICAgaHRtbCwgYm9keSB7CiAgICAgICAgICAgICAgICBoZWlnaHQ6IDEwMCU7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICA8L3N0eWxlPgogICAgPCEtLSBDdXN0b20gQ1NTIC0tPgo8L2hlYWQ+Cgo8Ym9keT4KPHNlY3Rpb24gY2xhc3M9ImNvbnRhaW5lciI+CiAgICA8ZGl2IGNsYXNzPSJjdXN0b21lci1sb2dvLXdyYXBwZXIiPgogICAgICAgIDxkaXYgY2xhc3M9ImN1c3RvbWVyLWxvZ28iPgogICAgICAgICAgICA8aW1nIHNyYz0iIiBhbHQ9IkxvZ28iLz4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0icGFnZS10aXRsZS13cmFwcGVyIj4KICAgICAgICA8ZGl2IGNsYXNzPSJwYWdlLXRpdGxlIj4KICAgICAgICAgICAgPGgxPlBsZWFzZSB2ZXJpZnkgeW91IGFyZSBhIGh1bWFuPC9oMT4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGVudC13cmFwcGVyIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250ZW50Ij4KICAgICAgICAgICAgPGRpdiBpZD0icHgtY2FwdGNoYSI+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgIEFjY2VzcyB0byB0aGlzIHBhZ2UgaGFzIGJlZW4gZGVuaWVkIGJlY2F1c2Ugd2UgYmVsaWV2ZSB5b3UgYXJlIHVzaW5nIGF1dG9tYXRpb24gdG9vbHMgdG8gYnJvd3NlIHRoZQogICAgICAgICAgICAgICAgd2Vic2l0ZS4KICAgICAgICAgICAgPC9wPgogICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgIFRoaXMgbWF5IGhhcHBlbiBhcyBhIHJlc3VsdCBvZiB0aGUgZm9sbG93aW5nOgogICAgICAgICAgICA8L3A+CiAgICAgICAgICAgIDx1bD4KICAgICAgICAgICAgICAgIDxsaT4KICAgICAgICAgICAgICAgICAgICBKYXZhc2NyaXB0IGlzIGRpc2FibGVkIG9yIGJsb2NrZWQgYnkgYW4gZXh0ZW5zaW9uIChhZCBibG9ja2VycyBmb3IgZXhhbXBsZSkKICAgICAgICAgICAgICAgIDwvbGk+CiAgICAgICAgICAgICAgICA8bGk+CiAgICAgICAgICAgICAgICAgICAgWW91ciBicm93c2VyIGRvZXMgbm90IHN1cHBvcnQgY29va2llcwogICAgICAgICAgICAgICAgPC9saT4KICAgICAgICAgICAgPC91bD4KICAgICAgICAgICAgPHA+CiAgICAgICAgICAgICAgICBQbGVhc2UgbWFrZSBzdXJlIHRoYXQgSmF2YXNjcmlwdCBhbmQgY29va2llcyBhcmUgZW5hYmxlZCBvbiB5b3VyIGJyb3dzZXIgYW5kIHRoYXQgeW91IGFyZSBub3QgYmxvY2tpbmcKICAgICAgICAgICAgICAgIHRoZW0gZnJvbSBsb2FkaW5nLgogICAgICAgICAgICA8L3A+CiAgICAgICAgICAgIDxwPgogICAgICAgICAgICAgICAgUmVmZXJlbmNlIElEOiAjZmQwMWU2ZDYtOWNmMi0xMWVlLTgwOGMtYWNkZTQ4MDAxMTIyCiAgICAgICAgICAgIDwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0icGFnZS1mb290ZXItd3JhcHBlciI+CiAgICAgICAgPGRpdiBjbGFzcz0icGFnZS1mb290ZXIiPgogICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgIFBvd2VyZWQgYnkKICAgICAgICAgICAgICAgIDxhIGhyZWY9Imh0dHBzOi8vd3d3LnBlcmltZXRlcnguY29tL3doeXdhc2libG9ja2VkIj5QZXJpbWV0ZXJYPC9hPgogICAgICAgICAgICAgICAgLCBJbmMuCiAgICAgICAgICAgIDwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L3NlY3Rpb24+CjwhLS0gUHggLS0+CjxzY3JpcHQ+CiAgICB3aW5kb3cuX3B4QXBwSWQgPSAnUFhqOXk0UThFbSc7CiAgICB3aW5kb3cuX3B4SnNDbGllbnRTcmMgPSAnLy9jbGllbnQucGVyaW1ldGVyeC5uZXQvUFhqOXk0UThFbS9tYWluLm1pbi5qcyc7CiAgICB3aW5kb3cuX3B4Rmlyc3RQYXJ0eUVuYWJsZWQgPSBmYWxzZTsKICAgIHdpbmRvdy5fcHhWaWQgPSAnOTI4ZDdhYjMtOWNmMS0xMWVlLWE2MjQtYjgwMjUyMGYzNjlmJzsKICAgIHdpbmRvdy5fcHhVdWlkID0gJ2ZkMDFlNmQ2LTljZjItMTFlZS04MDhjLWFjZGU0ODAwMTEyMic7CiAgICB3aW5kb3cuX3B4SG9zdFVybCA9ICcvL2NvbGxlY3Rvci1QWGo5eTRROEVtLnBlcmltZXRlcngubmV0JzsKPC9zY3JpcHQ+CjwhLS0gQ2FwdGNoYSAtLT4KPHNjcmlwdD4KICAgIHZhciBzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7CiAgICBzLnNyYyA9ICcvL2NhcHRjaGEucGVyaW1ldGVyeC5uZXQvUFhqOXk0UThFbS9jYXB0Y2hhLmpzP2E9YyZtPTEmdT1mZDAxZTZkNi05Y2YyLTExZWUtODA4Yy1hY2RlNDgwMDExMjImdj05MjhkN2FiMy05Y2YxLTExZWUtYTYyNC1iODAyNTIwZjM2OWYnOwogICAgdmFyIHAgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaGVhZCcpWzBdOwogICAgcC5pbnNlcnRCZWZvcmUocywgbnVsbCk7CiAgICBpZiAoZmFsc2UpIHsKICAgICAgICBzLm9uZXJyb3IgPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgICAgIHMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICAgICAgdmFyIHN1ZmZpeEluZGV4ID0gJy8vY2FwdGNoYS5wZXJpbWV0ZXJ4Lm5ldC9QWGo5eTRROEVtL2NhcHRjaGEuanM/YT1jJm09MSZ1PWZkMDFlNmQ2LTljZjItMTFlZS04MDhjLWFjZGU0ODAwMTEyMiZ2PTkyOGQ3YWIzLTljZjEtMTFlZS1hNjI0LWI4MDI1MjBmMzY5ZicuaW5kZXhPZignY2FwdGNoYS5qcycpOwogICAgICAgICAgICB2YXIgdGVtcGVyZWRCbG9ja1NjcmlwdCA9ICcvL2NhcHRjaGEucGVyaW1ldGVyeC5uZXQvUFhqOXk0UThFbS9jYXB0Y2hhLmpzP2E9YyZtPTEmdT1mZDAxZTZkNi05Y2YyLTExZWUtODA4Yy1hY2RlNDgwMDExMjImdj05MjhkN2FiMy05Y2YxLTExZWUtYTYyNC1iODAyNTIwZjM2OWYnLnN1YnN0cmluZyhzdWZmaXhJbmRleCk7CiAgICAgICAgICAgIHMuc3JjID0gJy8vY2FwdGNoYS5weC1jZG4ubmV0L1BYajl5NFE4RW0vJyArIHRlbXBlcmVkQmxvY2tTY3JpcHQ7CiAgICAgICAgICAgIHAucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUocywgcCk7CiAgICAgICAgfTsKICAgIH0KPC9zY3JpcHQ+CjwhLS0gQ3VzdG9tIFNjcmlwdCAtLT4KPC9ib2R5Pgo8L2h0bWw+Cg==", "appId": "PXj9y4Q8Em", "action": "captcha", "collectorUrl": "https://collector-pxj9y4q8em.perimeterx.net" }
-
The JSON contains metadata for the SDK.
-
Your app should pass the whole JSON to the SDK via the HSBotDefender/handleResponse(response:data:callback:) function. Otherwise, the SDK won't present a challenge to the user.
Updated 7 days ago