@ppl-sokratech-sdk/ppl-a4-sdk-react-native
v0.1.1
Published
SDK for react native
Readme
Sokratech React Native SDK
Behavioral biometrics, device fingerprint, and bot detection SDK for React Native (Android, iOS, Web). Sibling implementation of the Sokratech Web SDK and Flutter SDK.
Features
- Behavioral tracking — touch, drag, scroll, input, lifecycle, sensors (accelerometer + gyroscope on mobile)
- Device fingerprinting — audio, canvas (real Bitmap on Android + UIGraphicsImageRenderer on iOS via TurboModule), graphics, fonts, device, screen
- Bot detection — emulator markers on mobile, webDriver markers on web
- React-first API — Provider, hooks, and Tracked component wrappers. Same DX as the Web SDK.
- Backend integration — POST
/ingestwith bundled signals, GET/sdk/v1/config/:workflowId/:profileIdfor recipe-driven enable/disable.
Installation
npm install @ppl-sokratech-sdk/ppl-a4-sdk-react-native react-native-device-info expo-sensorsPeer dependencies:
reactandreact-native(any version compatible with your app)react-native-device-info(used for hardware, network, and emulator detection)
Optional but recommended:
expo-sensors(for accelerometer/gyroscope tracking; SDK soft-loads it if available)
For consuming as a sibling source folder during development (no npm publish required), see the PoC repo's scripts/sync-sdk.ps1 + sdk-bundled workflow.
Quick start (React API, recommended)
Wrap your app with SokratechProvider. Native modules (sensors, lifecycle) auto-hook on mount and clean up on unmount.
import { SokratechProvider, useSokratech } from '@ppl-sokratech-sdk/ppl-a4-sdk-react-native';
export default function App() {
return (
<SokratechProvider
config={{
apiDomain: 'https://api.example.com',
workflowId: '<workflow-uuid>',
profileId: '<profile-uuid>',
profiling: { enabled: true },
}}
fallback={<Text>Initializing SDK...</Text>}
>
<YourScreens />
</SokratechProvider>
);
}
function YourScreens() {
const { sdk, isInitialized } = useSokratech();
if (!isInitialized) return null;
// sdk is ready
}Hooks
import {
useBehavioral, // { collector, drain }
useFingerprint, // { collector, collect, clearCache, isCached }
useDetection, // { collector, detect }
useFlushIngest, // { flush, response, loading, error }
useSokratech, // { sdk, isInitialized, initError }
} from '@ppl-sokratech-sdk/ppl-a4-sdk-react-native';
function LoginForm() {
const { collect } = useFingerprint();
const { flush, response, loading } = useFlushIngest();
const submit = async () => {
await collect(true); // forces fresh fingerprint
const result = await flush(); // POST /ingest, returns decision
if (result?.ok) {
console.log('decision:', result.data.decision);
}
};
return <Button onPress={submit} disabled={loading} title="Login" />;
}Tracked component wrappers
Drop-in replacements that auto-wire to the behavioral collector. No manual onFocus/onTouchStart/onScroll plumbing.
import {
TrackedTextInput,
TrackedPressable,
TrackedScrollView,
} from '@ppl-sokratech-sdk/ppl-a4-sdk-react-native';
<TrackedTextInput trackId="login-username" value={u} onChangeText={setU} />
<TrackedPressable onPress={onSubmit}><Text>Submit</Text></TrackedPressable>
<TrackedScrollView>{/* content */}</TrackedScrollView>Procedural API (alternative)
If you don't want to use React Context, use the SDK class directly:
import { SokratechSDK } from '@ppl-sokratech-sdk/ppl-a4-sdk-react-native';
const sdk = await SokratechSDK.initAsync({
apiDomain: 'https://api.example.com',
workflowId: '<workflow-uuid>',
profileId: '<profile-uuid>',
});
sdk.getBehavioralCollector()?.touchTracker?.onTouchStart(1, x, y, ts);
const response = await sdk.flushIngest();
if (response.ok) {
console.log('decision:', response.data);
}
sdk.destroy();API surface
// Factory
SokratechSDK.init(config) // synchronous, uses local recipes
SokratechSDK.initAsync(config) // async, fetches remote recipes with fallback
// Status
sdk.isInitialized(): boolean
sdk.getConfig(): SokratechConfig
// Collectors
sdk.getBehavioralCollector(): BehavioralCollector | null
sdk.getFingerprintCollector(): FingerprintCollector | null
sdk.getDetectionCollector(): DetectionCollector | null
// Convenience
sdk.drainBehavioralEvents(): BehavioralPayload | null
sdk.collectFingerprint(force?): Promise<CollectedFingerprint | null>
sdk.detect(force?): Promise<DetectionResult | null>
sdk.flushIngest(): Promise<IngestApiResponse>
// Profiling
sdk.getProfileMetrics(): ProfileMetric[]
sdk.clearProfileMetrics(): void
// Lifecycle
sdk.destroy(): voidConfiguration
interface SokratechConfig {
apiDomain: string;
ingestEndpoint?: string; // defaults to "/ingest"
workflowId?: string;
profileId?: string;
recipes?: RecipeOptions; // overrides remote recipe fetch when set
profiling?: ProfilerConfig;
}When workflowId and profileId are both set, initAsync fetches the recipe from GET /sdk/v1/config/:workflowId/:profileId. If the request fails or the IDs are absent, the SDK falls back to all-true recipes provided locally.
Native modules
The SDK ships a TurboModule (PplA4SdkReactNative) for two surfaces that need platform code:
getCanvasFingerprint()— renders text + geometry images viaandroid.graphics.Canvas/UIGraphicsImageRenderer, returns base64 PNG data URLs matching the Web SDK shape.getInstalledFonts()— Android scans/system/fonts,/system/font,/data/fonts; iOS enumeratesUIFont.familyNamesandUIFont.fontNamesForFamilyName.
Both fall back to platform-static lists if the TurboModule is not registered (graceful degradation).
Fingerprint surfaces
| Surface | Web | Android | iOS |
|---|---|---|---|
| audio | OfflineAudioContext signal window | {sampleRate: 48000, sampleCount: 512, channels: 2} | same as Android |
| canvas | data:image/png;base64,... from HTMLCanvas | data:image/png;base64,... from Bitmap (TurboModule) | data:image/png;base64,... from UIGraphicsImageRenderer (TurboModule) |
| graphics | WebGL getParameter queries | Dimensions + Platform info | same as Android |
| fonts | font matrix probe | /system/fonts scan (TurboModule, fallback to static list) | UIFont.familyNames (TurboModule, fallback to static list) |
| device | navigator properties | react-native-device-info (brand, model, deviceId, CPU, RAM) | same as Android |
| screen | globalThis.screen | Dimensions.get('screen') in pixels | same as Android |
Development
npm install
npm run typecheck
npm run lint
npm test # 509 tests
npm run test:coverage # 100% global threshold enforcedBuild the library for publishing:
npm run prepare # runs react-native-builder-bob, outputs to lib/TDD convention
Commit messages follow [RED] / [GREEN] / [REFACTOR] prefix on top of conventional commits, enforced by commitlint:
[RED] test(api/http): cover request helpers
[GREEN] feat(api/http): add sendJsonRequest
[REFACTOR] refactor(api/http): extract resolveFetchCI expectations
GitHub Actions enforces, on every push / PR:
- TypeScript typecheck
- ESLint + Prettier
- Jest tests with 100% global coverage threshold (branches, functions, lines, statements)
- SonarQube quality gate (no new issues, 100% coverage, 0 security hotspots)
Publishing
Releases use release-it with conventional changelog:
npm run release # interactive: bump version, tag, push, publish to npmrelease-it requires npm login and git working tree clean.
License
MIT
