@gatekeeperx/device-intelligence-web
v0.2.5
Published
GatekeeperX Device Intelligence SDK for Web (isomorphic, SSR+Edge compatible). Browser-native homologue of the Android SDK V_6 fingerprint.
Maintainers
Readme
@gatekeeperx/device-intelligence-web
Web SDK for GatekeeperX Device Intelligence — browser-native homologue of the Android SDK com.gatekeeperx:devicex (V_6 fingerprint).
Recolecta ~22 señales del navegador, produce un fingerprint determinista (SHA-256) y envía eventos firmados con HMAC-SHA256 al backend de GatekeeperX. Mismo payload, mismos endpoints y mismas firmas que el SDK Android: el servidor no requiere cambios.
Instalación
npm install @gatekeeperx/device-intelligence-webQuickstart
import { DeviceIntelligence } from '@gatekeeperx/device-intelligence-web';
DeviceIntelligence.configure({
apiKey: 'your-api-key',
tenant: 'your-tenant',
environment: 'sandbox',
});
const result = await DeviceIntelligence.sendEvent('login', { userId: 'u_123' });
if (result.kind === 'success') {
console.log('deviceXId:', result.deviceXId);
} else {
console.warn(result.errorCode, result.errorMessage);
}DSL completo
DeviceIntelligence.configure({
apiKey: 'sk_xxx',
tenant: 'rappi',
environment: 'production', // 'production' | 'sandbox' | 'development'
organizationId: 'rappi-co', // opcional, default = tenant
headers: { 'X-App-Version': '1.0.0' },
fingerprintVersion: 'V_6', // default
stabilityLevel: 'OPTIMAL', // 'STABLE' | 'OPTIMAL' | 'UNIQUE'
optIns: {
location: true, // geolocalización GPS (prompt al usuario)
wifi: true, // IP local vía WebRTC
behavioral: true, // biometría conductual (mouse + teclado)
},
timeoutMs: 30_000,
apiBaseUrl: 'https://custom.endpoint.io', // opcional, sobreescribe baseUrl por environment
});API
| Método | Tipo | Descripción |
|---|---|---|
| configure(opts) | void | Inicializa el singleton. Lanza si ya está configurado. |
| sendEvent(name, properties?, headers?) | Promise<EventResult> | Envía evento firmado. Nunca rechaza la promesa. |
| sendEventAsync(name, properties?, headers?, callback?) | void | Fire-and-forget con callback opcional. |
| getFingerprint(version?, stability?) | Promise<string> | Hash hex de 64 chars. |
| getDeviceId() | Promise<DeviceIdResult> | Fingerprint con señales STABLE únicamente. |
| getRiskAssessment() | Promise<RiskAssessment> | Detección de headless/automation/webdriver/incógnito/debugger. |
| isInitialized() | boolean | true solo en browser después de configure(). |
| getVersion() | string | Versión del SDK. |
| shutdown() | void | Resetea el singleton y detiene la recolección conductual. |
DeviceIdResult
interface DeviceIdResult {
deviceId: string; // fingerprint hex-64 usando solo señales STABLE
signalCount: number; // número de señales STABLE usadas
}EventResult
EventResult es discriminated union:
type EventResult =
| { kind: 'success'; code: number; deviceXId: string; message?: string }
| { kind: 'failure'; errorCode: ErrorCode; errorMessage: string; httpCode?: number };ErrorCode
| Código | Descripción |
|---|---|
| SDK_NOT_INITIALIZED | configure() no fue llamado antes del evento. |
| INVALID_EVENT_NAME | Nombre de evento vacío o con caracteres inválidos. |
| INVALID_PROPERTIES | Properties contiene tipos no soportados. |
| PII_DETECTED | Se detectó PII en el payload (email, CURP, etc.). |
| PAYLOAD_TOO_LARGE | El payload serializado supera el límite de tamaño. |
| UNSUPPORTED_DATA_TYPE | Tipo de dato no soportado en properties. |
| RATE_LIMIT_EXCEEDED | Se superaron 100 eventos/segundo (token bucket). |
| NETWORK_ERROR | Error de red o CORS al enviar el evento. |
| TIMEOUT | La solicitud superó timeoutMs. |
| SERVER_ERROR | El servidor respondió con HTTP ≥ 500. |
| SERIALIZATION_ERROR | Fallo al serializar el payload a JSON. |
| UNKNOWN_ERROR | Error no clasificado. |
Señales recolectadas (V_6)
| Categoría | Señales |
|---|---|
| UA / Plataforma | userAgentBrandsSignal, platformSignal (UA Client Hints) |
| Hardware | hardwareConcurrencySignal, deviceMemorySignal, touchPointsSignal |
| Display | screenSignal (width/height/colorDepth/orientation/pixelRatio) |
| Locale | timezoneSignal, languagesSignal, localeSignal |
| Gráficos | canvasFingerprintSignal, webglRendererSignal |
| Audio | audioContextFingerprintSignal |
| Fuentes | fontListSignal (~40 fuentes detectadas) |
| Privacidad | cookiesEnabledSignal, doNotTrackSignal, gpcSignal |
| Storage | storageQuotaSignal |
| Red | connectionSignal (effectiveType/downlink/rtt) |
| Sensores | sensorsSignal, batterySignal |
| Plugins | pluginsSignal |
| Location | locationSignals (opt-in) con detección de spoofing |
| WiFi | wifiSignals (opt-in, vía WebRTC para IP local) |
| VPN | vpnSignals (heurísticas: timezone/locale mismatch, RTT anómalo, geo-IP via ipapi.co) |
| Risk | riskSignals (webdriver, automation hooks, incógnito, debugger adjunto) |
| Behavioral | behavioralSignals (opt-in, biometría conductual — ver sección siguiente) |
Biometría conductual (opt-in)
Al pasar optIns.behavioral: true, el SDK activa un BehaviorCollector que escucha eventos DOM pasivos y construye un BehaviorSnapshot al momento de enviar un evento.
Privacidad: el colector nunca registra el valor de teclas ni caracteres escritos (
e.keyye.datano se leen). Solo se capturan tiempos y conteos. Si el usuario tiene activo DNT o GPC, el snapshot retornaprivacyRedacted: truecon todos los contadores en cero.
BehaviorSnapshot
| Campo | Tipo | Descripción |
|---|---|---|
| moveCount | number | Eventos pointermove procesados (throttle 50 ms). |
| hadPointerActivity | boolean | true si hubo movimiento o clic. |
| avgVelocity | number | Velocidad media del puntero (px/ms). |
| velocityStdDev | number | Desviación estándar de velocidad. |
| straightLineRatio | number | 0–1; ratio de movimientos perfectamente rectos (indica scripting). |
| clickCount | number | Total de clicks/taps. |
| avgClickInterval | number | Intervalo medio entre clicks (ms). |
| keyCount | number | Total de teclas soltadas (keyup). |
| avgDwellTime | number | Tiempo medio de pulsación (keydown→keyup, ms). |
| dwellTimeStdDev | number | Desviación estándar del dwell time. |
| avgFlightTime | number | Intervalo medio entre keyup consecutivos (ms). |
| avgDownDownTime | number | Intervalo medio entre keydowns consecutivos (ms). |
| maxDownDownTime | number | Máximo down-down interval observado (ms). |
| keyOverlapCount | number | Teclas pulsadas simultáneamente (rollover). |
| pasteCount | number | Eventos paste (Ctrl+V, Cmd+V, menú contextual). |
| copyCount | number | Eventos copy. |
| cutCount | number | Eventos cut. |
| backspaceCount | number | Pulsaciones de Backspace. |
| deleteCount | number | Pulsaciones de Delete. |
| tabCount | number | Pulsaciones de Tab. |
| enterCount | number | Pulsaciones de Enter/NumpadEnter. |
| arrowKeyCount | number | Pulsaciones de teclas de dirección. |
| shortcutCount | number | Combinaciones con Ctrl/Meta/Alt. |
| untrustedEventCount | number | Eventos con isTrusted === false (sintéticos/script). |
| programmaticInputCount | number | Inputs sin keydown previo (autofill, password manager, bot). |
| correctionRate | number | (backspace + delete) / keyCount. |
| typingSpeedCpm | number | Caracteres por minuto estimados. |
| inputEventCount | number | Eventos beforeinput de tipo inserción (IME/móvil). |
| compositionCount | number | Composiciones IME completadas. |
| sessionDurationMs | number | Duración de sesión desde configure() (ms). |
| timeToFirstInteraction | number | Milisegundos hasta el primer evento de usuario; -1 si no hubo. |
| privacyRedacted | boolean | true si DNT o GPC activo; todos los demás campos = 0. |
Stability levels
STABLE— señales no-volátiles (platform, hardware, screen, timezone, languages, webglVendor, touchPoints, cookies). Para identidad longitudinal (≥ días).OPTIMAL(default) — STABLE + canvas, webglRenderer, audio, fontList, plugins, storageQuota, connection. Balance estabilidad/unicidad.UNIQUE— OPTIMAL + sensors, battery, gpc, dnt. Máxima unicidad pero menos estable.
RiskAssessment
interface RiskAssessment {
isHeadless: boolean; // navegador sin UI (Chrome headless, JSDOM, etc.)
isAutomation: boolean; // hooks de automatización detectados (Playwright, Puppeteer)
isWebdriver: boolean; // navigator.webdriver === true
isPrivacyMode: boolean; // modo incógnito / ventana privada
isDebuggerAttached: boolean; // DevTools adjuntos al momento del evento
indicators: string[]; // evidencia cruda: e.g. ['webdriver', 'privacy_mode']
}El SDK reporta señales crudas, no un score ni un veredicto. El cálculo de riesgo se realiza server-side.
SSR (Next.js, Remix, Nuxt)
El import es seguro en Server Components. En server:
isBrowser()→falseconfigure()registra config pero no recolecta señales.isInitialized()→false.sendEvent()→EventResult.failure('SDK_NOT_INITIALIZED').getFingerprint()→''.
Patrón recomendado en Next.js:
'use client';
import { useEffect } from 'react';
import { DeviceIntelligence } from '@gatekeeperx/device-intelligence-web';
export function DeviceIntelligenceBoot({ apiKey, tenant }: { apiKey: string; tenant: string }) {
useEffect(() => {
if (!DeviceIntelligence.isInitialized()) {
DeviceIntelligence.configure({ apiKey, tenant, environment: 'sandbox' });
}
}, [apiKey, tenant]);
return null;
}Permisos opt-in
| Opt-in | Default | Descripción |
|---|---|---|
| location | false | Geolocalización GPS. Requiere prompt al usuario. Si denegado, locationSignals = {} y meta.locationStatus = 'not_requested'. |
| wifi | false | IP local via WebRTC ICE candidates. Si desactivado, wifiSignals = {}. |
| behavioral | false | Biometría conductual (mouse + teclado + clipboard). Respeta DNT/GPC automáticamente. |
Rate limiting
El SDK aplica un token bucket de 100 eventos/segundo por instancia. Si se supera el límite, sendEvent() retorna inmediatamente con errorCode: 'RATE_LIMIT_EXCEEDED' sin realizar ninguna solicitud de red.
Detección de VPN
vpnSignals se construye combinando:
- Heurísticas locales: mismatch timezone/locale, RTT anómalo.
- Geo-IP vía ipapi.co (free, CORS habilitado, 1000 req/día por IP). El resultado se cachea por sesión. Si el fetch falla o hace timeout (3 s), el detector cae a las heurísticas locales.
Uso avanzado
DeviceIntelligenceAgent (inyección de dependencias / testing)
El singleton DeviceIntelligence es un wrapper sobre DeviceIntelligenceAgent. Para tests o integraciones que requieren múltiples instancias independientes:
import { DeviceIntelligenceAgent, buildConfig } from '@gatekeeperx/device-intelligence-web';
const config = buildConfig({ apiKey: 'sk_xxx', tenant: 'acme' });
const agent = new DeviceIntelligenceAgent(config, {
// Inyectar dependencias mock para testing:
// eventClient, rateLimiter, payloadBuilder, etc.
});
const result = await agent.sendEvent('checkout');
agent.destroy(); // detiene BehaviorCollectorbuildConfig
Valida y construye un DeviceIntelligenceConfig inmutable a partir de ConfigureOptions. Útil para pre-validar la configuración antes de instanciar el agente.
Compatibilidad
- Navegadores: Chrome/Edge ≥ 80, Firefox ≥ 88, Safari ≥ 14.
- Node: ≥ 18 (para SSR y tests).
- Bundlers: webpack, vite, parcel, esbuild, rollup, Next.js, Remix, Nuxt, Angular CLI, Vue CLI.
- JavaScript puro: sí, ignora los
.d.ts.
Privacidad
- ❌ No envía MAC, IMEI, IP pública, biometría biológica, contactos.
- ❌ No registra qué teclas se pulsaron, solo tiempos y conteos.
- ✅ Location se redondea a 2 decimales (~1 km de precisión).
- ✅ Permisos opt-in para Location, WiFi y biometría conductual.
- ✅ Compatible con GPC (Global Privacy Control): behavioral snapshot retorna
privacyRedacted: true. - ✅ Compatible con DNT (Do Not Track): misma protección que GPC.
Licencia
MIT © GatekeeperX
