Zum Hauptinhalt springen
Version: Guardian v0.1.0

NPM-Paket

@surtai/guardian-web · v0.3.1

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.

Verwendung

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

// Variante 1: keine IP-Abfrage, null Netzwerkaufrufe.
const { payload } = await collect({ collectLocation: false });

// Variante 2: öffentliche IP des Browsers in das Payload auflösen.
// Dein Backend erzeugt das kurzlebige JWT via Preflight.
const { payload: payloadWithIp } = await collect({
collectLocation: false,
geolocationJwt: jwt,
});

// Leite das Payload an dein Backend weiter.
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. Übergib es als `payload.data` in der unten gezeigten Evaluate-Anfrage. */
payload: string;
/** Was das SDK während der Erfassung beobachtet hat — additiv, kann ignoriert werden. */
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') {
// den Nutzer auffordern, den Standort zu aktivieren, dann erneut versuchen
}

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 von 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': /* nicht in einem sicheren Kontext */ break;
case 'ENCRYPTION_FAILED': /* Verschlüsselung fehlgeschlagen (selten) */ break;
case 'INVALID_OPTIONS': /* ungültige Argumente */ 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.