Zum Hauptinhalt springen
Version: Guardian Web v0.5.0

NPM-Paket

@surtai/guardian-web · v0.5.0

Das SDK @surtai/guardian-web führt Guardians collect()-Ablauf in jedem modernen Browser aus. Es erfasst Gerätesignale, verschlüsselt sie lokal und liefert ein undurchsichtiges Payload, das dein Backend an Surts Evaluate-Endpoint sendet. Das SDK macht keine Netzwerkaufrufe und hält keinen API-Schlüssel - mit einer Ausnahme: Wenn du ein optionales geolocationJwt übergibst, führt collect() einen einzelnen Best-Effort-Aufruf GET /geolocation/client-ip aus, um die öffentliche IP des Browsers aufzulösen und sie in das Payload einzubetten. Ohne geolocationJwt macht collect() null Netzwerkaufrufe. Das JWT ist ein kurzlebiges Token, das von deinem Backend erzeugt wird, kein API-Schlüssel - es gibt also weiterhin keinen API-Schlüssel im Browser.

npm install @surtai/guardian-web
Web ist collect-only

Anders als die nativen SDKs (iOS / Android / React Native) stellt das Web-SDK keine verify()-Methode bereit. Alle Risikoentscheidungen erfolgen serverseitig, sobald dein Backend das Payload weiterleitet. Siehe Collect (Server-zu-Server) für den vollständigen Backend-Ablauf.

Geräteerkennung (v0.5.0)​

Ab v0.5.0 fügt das SDK dem verschlüsselten Payload automatisch zwei Signale hinzu - ohne Codeänderung auf deiner Seite:

  • Stabile Geräteerkennung - das SDK erzeugt und persistiert eine install id pro Browser und nimmt sie in collect() auf. Surt nutzt sie, um ein wiederkehrendes Gerät über mehrere Besuche hinweg zu erkennen, sodass derselbe Browser demselben Gerät zugeordnet wird, selbst wenn sein roher Fingerabdruck mit anderen Maschinen desselben Modells kollidiert.
  • Meldung der SDK-Version - das SDK meldet seine eigene Build-Version, sodass Surt jede Transaktion dem exakten SDK-Release zuordnen kann.

Beide reisen innerhalb des verschlüsselten Payloads - du liest oder setzt sie nie. Ein Upgrade des Pakets ist alles, was nötig ist, um zu profitieren; siehe Migration v0.4 → v0.5.

Dauerhafte Erkennung in Safari

Browser wie Safari löschen skriptbeschreibbaren Speicher nach ~7 Tagen, was die install id rotiert. Damit die Erkennung dort stabil bleibt, kann dein Backend die id in einem First-Party- Cookie persistieren, das über Preflight weitergereicht wird - eine optionale ~5-zeilige Änderung, die im Migrationsleitfaden behandelt wird.

Verwendung​

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 }),
});

Dein Backend erzeugt das geolocationJwt, indem es Preflight mit deinem sp_live_*-Schlüssel aufruft - siehe Authentifizierung. Übergib das JWT an collect() nur, wenn die öffentliche IP aufgelöst werden soll; andernfalls lass es weg.

Optionen​

OptionTypErforderlichStandardBeschreibung
collectLocationbooleanNeinfalseWenn true, wird die Geolocation-API des Browsers angefordert. Das ist die einzige Stelle in collect(), die einen Berechtigungsdialog auslösen kann.
geolocationJwtstringNein(keiner)Wenn angegeben, löst collect() die öffentliche IP des Browsers via GET /geolocation/client-ip auf und bettet sie in das Payload ein. Weglassen, um die IP-Abfrage (und alle Netzwerkaufrufe) zu überspringen.

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 }[];
};
}

Das diagnostics-Objekt zeigt dir, was auf dem Gerät passiert ist - zum Beispiel, ob der Standort erfasst wurde oder der Nutzer ihn abgelehnt hat - sodass du in deiner UI reagieren kannst:

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

Siehe Ergebnis-Diagnose für die vollständige Feldreferenz.

Backend: an Surt weiterleiten​

Dein Backend empfängt das Payload vom Browser und sendet es an Surts Evaluate-Endpoint:

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" }
}
}
}

Die Form der Anfrage, die unterstützten transaction_type-Werte, die config-Optionen und das Antwortschema sind in allen Guardian-SDKs identisch. Siehe Collect (Server-zu-Server) für die vollständige Referenz, einschließlich Beispielen in Node / Java / Python.

Fehler​

collect() wirft einen einzelnen GuardianError in drei Fällen:

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;
}
}
}
CodeBedeutung
CRYPTO_UNAVAILABLEwindow.crypto.subtle fehlt. Das SDK benötigt einen sicheren Kontext (HTTPS oder localhost).
ENCRYPTION_FAILEDDer Verschlüsselungsschritt ist fehlgeschlagen. Wie einen Bug behandeln: erfassen und melden.
INVALID_OPTIONSDas Argument von collect() ist kein Objekt.

Einzelne Collectors (Fingerabdruck, Akku, Geolocation, Netzwerk, etc.) werfen niemals: sie scheitern lautlos. Eine entzogene Berechtigung oder eine nicht unterstützte API bedeutet nur, dass das entsprechende Feld aus dem Payload weggelassen wird. Auch die Abfrage der öffentlichen IP ist Best-Effort: Wenn der Aufruf GET /geolocation/client-ip fehlschlägt oder das JWT abgelehnt wird, wird das IP-Feld einfach weggelassen und collect() gibt trotzdem ein Payload zurück. Das Backend toleriert sparsame Payloads.

Browser-Unterstützung​

  • Jeder moderne Browser mit window.crypto.subtle (Chrome, Edge, Firefox, Safari, Opera).
  • Sicherer Kontext erforderlich: HTTPS in der Produktion, localhost für die Entwicklung. Das SDK wirft CRYPTO_UNAVAILABLE außerhalb eines sicheren Kontexts.
  • Geolocation, Battery Status, NetworkInformation und andere optionale APIs degradieren elegant, wenn sie nicht verfügbar sind.

Framework-Beispiele​

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 ? 'Wird geprüft...' : 'Weiter'}
</button>
);
}

Unterschiede zu den nativen SDKs​

@surtai/guardian-web@surtai/guardian-rn / iOS / Android
verify()-MethodeNeinJa
collect()-MethodeJa (einzige API)Ja
App-Level-InitialisierungKeineinitialize(options)
Kunden- / TransaktionskontextVom Backend gesetztWird in den Claims des vom Backend erzeugten JWT mitgeführt
API-Schlüssel im ClientNeinNein (verwendet ein vom Backend erzeugtes JWT, keinen API-Schlüssel)
Netzwerkaufrufe vom SDKKeine (außer wenn geolocationJwt übergeben wird)Ja
GeräteintegritätsprüfungNein (kein Browser-Äquivalent)Ja
Persistenter KundenzustandNein (zustandslos pro Aufruf)Nein

Web ist bewusst schlank: eine einzige statische Funktion, die ein verschlüsseltes Payload liefert. Kundenbindung, Transaktionsmetadaten und Entscheidungen passieren alle in deinem Backend.