Saltar al contenido principal
Version: Guardian Web v0.5.0

Paquete NPM

@surtai/guardian-web · v0.5.0

El SDK @surtai/guardian-web ejecuta el flujo collect() de Guardian en cualquier navegador moderno. Recopila señales del dispositivo, las cifra localmente y devuelve un payload opaco que tu backend envía al endpoint de evaluación de Surt. El SDK no realiza llamadas de red ni contiene ninguna clave de API - con una excepción: cuando pasas un geolocationJwt opcional, collect() realiza una única llamada de mejor esfuerzo GET /geolocation/client-ip para resolver la IP pública del navegador e incrustarla en el payload. Sin geolocationJwt, collect() realiza cero llamadas de red. El JWT es un token de corta duración generado por tu backend, no una clave de API, por lo que sigue sin haber ninguna clave de API en el navegador.

npm install @surtai/guardian-web
La versión web es solo de recopilación

A diferencia de los SDKs nativos (iOS / Android / React Native), el SDK web no expone un método verify(). Todas las decisiones de riesgo ocurren del lado del servidor una vez que tu backend reenvía el payload. Consulta Collect (Servidor a servidor) para el flujo completo del backend.

Reconocimiento del dispositivo (v0.5.0)​

A partir de v0.5.0 el SDK añade dos señales al payload cifrado automáticamente - sin ningún cambio de código de tu parte:

  • Reconocimiento estable del dispositivo - el SDK genera y persiste un install id por navegador y lo incluye en collect(). Surt lo usa para reconocer un dispositivo recurrente entre visitas, de modo que el mismo navegador se resuelve al mismo dispositivo incluso cuando su huella digital cruda colisiona con otras máquinas del mismo modelo.
  • Reporte de la versión del SDK - el SDK reporta su propia versión de compilación, para que Surt pueda atribuir cada transacción a la versión exacta del SDK.

Ambas viajan dentro del payload cifrado - nunca las lees ni las estableces. Actualizar el paquete es todo lo que se requiere para beneficiarse; consulta Migrar de v0.4 → v0.5.

Reconocimiento duradero en Safari

Navegadores como Safari eliminan el almacenamiento escribible por scripts tras ~7 días, lo que rota el install id. Para mantener el reconocimiento estable ahí, tu backend puede persistir el id en una cookie de origen propio (first-party) retransmitida a través de preflight - un cambio opcional de ~5 líneas cubierto en la guía de migración.

Uso​

TypeScript
import { collect } from '@surtai/guardian-web';

// Variant 1: no IP lookup, zero network calls.
const { payload } = await collect({ collectLocation: false });

// Variant 2: resolve the browser's public IP into the payload.
// Your backend mints the short-lived JWT via preflight.
const { payload: payloadWithIp } = await collect({
collectLocation: false,
geolocationJwt: jwt,
});

// Forward the payload to your backend.
await fetch('https://your-api.com/verify-device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: 'user_123', payload }),
});

Tu backend genera el geolocationJwt llamando a preflight con tu clave sp_live_* - consulta Autenticación. Pasa el JWT a collect() solo cuando quieras que se resuelva la IP pública; de lo contrario, omítelo.

Opciones​

OpciónTipoRequeridoPredeterminadoDescripción
collectLocationbooleanNofalseCuando es true, solicita la API de Geolocalización del navegador. Lo único en collect() que puede activar una solicitud de permiso.
geolocationJwtstringNo(ninguno)Cuando se proporciona, collect() resuelve la IP pública del navegador mediante GET /geolocation/client-ip y la incrusta en el payload. Omítelo para saltar la búsqueda de IP (y todas las llamadas de red).

CollectResult​

interface CollectResult {
/** Base64 payload. Pass as `payload.data` in the evaluate request shown below. */
payload: string;
/** What the SDK observed while collecting - additive, safe to ignore. */
diagnostics: {
location?: 'collected' | 'denied' | 'unavailable' | 'timeout' | 'not_requested';
networkIntel?: 'collected' | 'unavailable' | 'not_requested';
warnings: { code: string; signal: string; detail?: string }[];
};
}

El objeto diagnostics te indica qué ocurrió en el dispositivo - por ejemplo, si se recopiló la ubicación o si el usuario la denegó - para que puedas reaccionar en tu interfaz:

const { payload, diagnostics } = await collect({ collectLocation: true });
if (diagnostics.location === 'denied') {
// prompt the user to enable location, then retry
}

Consulta Diagnósticos del resultado para la referencia completa de campos.

Backend: reenviar a Surt​

Tu backend recibe el payload del navegador y lo envía al endpoint de evaluación de Surt:

POST https://api.surt.com/geolocation/transactions/evaluate
Content-Type: application/json
Authorization: Bearer YOUR_SURT_API_KEY
{
"customer_id": "user_123",
"transaction_type": "login",
"transaction_name": "Sign in",
"payload": {
"type": "encrypted",
"data": "<payload from collect()>"
},
"config": {
"response": {
"address": { "type": "include" }
}
}
}

La forma de la solicitud, los valores de transaction_type admitidos, las opciones de config y el esquema de respuesta son idénticos en todos los SDKs de Guardian. Consulta Collect (Servidor a servidor) para la referencia completa, incluyendo ejemplos en Node / Java / Python.

Errores​

collect() lanza un único GuardianError para tres condiciones:

import { collect, GuardianError } from '@surtai/guardian-web';

try {
const { payload } = await collect();
} catch (err) {
if (err instanceof GuardianError) {
switch (err.code) {
case 'CRYPTO_UNAVAILABLE': /* not in a secure context */ break;
case 'ENCRYPTION_FAILED': /* encryption failed (rare) */ break;
case 'INVALID_OPTIONS': /* bad arguments */ break;
}
}
}
CódigoSignificado
CRYPTO_UNAVAILABLEFalta window.crypto.subtle. El SDK requiere un contexto seguro (HTTPS o localhost).
ENCRYPTION_FAILEDEl paso de cifrado falló. Trátalo como un error: captúralo y repórtalo.
INVALID_OPTIONSEl argumento de collect() no es un objeto.

Los recopiladores individuales (huella digital, batería, geolocalización, red, etc.) nunca lanzan errores: fallan de forma silenciosa. Un permiso revocado o una API no compatible simplemente significa que el campo correspondiente se omite del payload. La búsqueda de IP pública también es de mejor esfuerzo: si la llamada GET /geolocation/client-ip falla o el JWT es rechazado, el campo de IP simplemente se omite y collect() aun así devuelve un payload. El backend tolera payloads incompletos.

Compatibilidad de navegadores​

  • Cualquier navegador evergreen con window.crypto.subtle (Chrome, Edge, Firefox, Safari, Opera).
  • Se requiere contexto seguro: HTTPS en producción, localhost para desarrollo. El SDK lanza CRYPTO_UNAVAILABLE fuera de un contexto seguro.
  • Geolocalización, Battery Status, NetworkInformation y otras APIs opcionales se degradan de forma elegante cuando no están disponibles.

Ejemplos por framework​

VerifyButton.tsx
import { useState } from 'react';
import { collect } from '@surtai/guardian-web';

export function VerifyButton({ userId }: { userId: string }) {
const [loading, setLoading] = useState(false);

const handleClick = async () => {
setLoading(true);
try {
const { payload } = await collect({ collectLocation: false });
await fetch('/api/verify-device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, payload }),
});
} finally {
setLoading(false);
}
};

return (
<button onClick={handleClick} disabled={loading}>
{loading ? 'Verifying...' : 'Continue'}
</button>
);
}

Qué es diferente respecto a los SDKs nativos​

@surtai/guardian-web@surtai/guardian-rn / iOS / Android
Método verify()NoSí
Método collect()Sí (única API)Sí
Inicialización a nivel de aplicaciónNingunainitialize(options)
Contexto de cliente / transacciónEstablecido por tu backendTransportado en los claims del JWT generado por el backend
Clave de API en el clienteNoNo (usa un JWT generado por el backend, no una clave de API)
Llamadas de red desde el SDKNinguna (a menos que se pase geolocationJwt)Sí
Verificación de integridad del dispositivoNo (sin equivalente en el navegador)Sí
Persiste estado por clienteNo (sin estado en cada llamada)No

La versión web es intencionadamente ligera: una única función estática que produce un payload cifrado. La vinculación del cliente, los metadatos de la transacción y las decisiones ocurren en tu backend.