iOS
Native iOS-Integration für das Surt Guardian SDK, vertrieben über Swift Package Manager. Das SDK erfasst Gerätesignale, führt Geräteintegritätsprüfungen durch und gibt die Risikoentscheidung des Backends zurück.
Das SDK hält niemals einen Surt-API-Schlüssel. Ihr Backend hält den sp_live_*-Schlüssel (nur serverseitig) und tauscht ihn gegen ein kurzlebiges JWT, das die App an verify() übergibt.
Dieser Leitfaden behandelt SDK v0.3.0, das auch die VPN-Erkennung korrigiert.
Voraussetzungen
- iOS 14.0+
- Xcode 14+
- Swift 5.7+
1. Authentifizierung
Fügen Sie Ihr Zugriffstoken zu ~/.netrc hinzu, damit Xcode das private Repository klonen kann:
machine github.com
login surt-customer
password <YOUR_TOKEN>
Ersetzen Sie <YOUR_TOKEN> durch das von Surt bereitgestellte Token.
Falls ~/.netrc nicht existiert, erstellen Sie es. Stellen Sie sicher, dass es eingeschränkte Berechtigungen hat:
chmod 600 ~/.netrc
2. Paket hinzufügen
- Gehen Sie in Xcode zu File > Add Package Dependencies
- Geben Sie die Repository-URL ein:
https://github.com/surtTech/surt-guardian-sdk - Setzen Sie die Versionsregel auf "Up to Next Minor" ab 0.3.0
- Klicken Sie auf Add Package
- Wählen Sie die SurtGuardianSDK-Bibliothek aus und fügen Sie sie Ihrem Target hinzu
3. Info.plist-Berechtigungen konfigurieren
Wenn Sie die Standorterfassung aktivieren, fügen Sie diesen Schlüssel zu Ihrer Info.plist hinzu:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location helps verify your identity and protect your account.</string>
Wenn Sie die Kamerainformationserfassung aktivieren, fügen Sie diesen Schlüssel hinzu:
<key>NSCameraUsageDescription</key>
<string>Camera access helps verify device authenticity.</string>
Das SDK fordert die entsprechende Berechtigung während der Verifizierung automatisch an, wenn die passende Erfassungsoption aktiviert ist. Der Systemdialog erscheint automatisch - es ist kein zusätzlicher Code erforderlich.
4. SDK initialisieren
Einmal beim App-Start aufrufen, vor jeder anderen SDK-Methode. Im SDK ist kein API-Schlüssel erforderlich - Ihr serverseitiger API-Schlüssel gelangt niemals auf das Gerät.
import SurtGuardianSDK
// In Ihrem AppDelegate oder App-Init:
GuardianSDK.initialize(
options: GuardianOptions(
environment: .production,
failurePolicy: .fail,
logLevel: .warn,
collectLocation: true // GPS-Erfassung aktivieren (optional)
)
)
Umgebungen
| Umgebung | Basis-URL |
|---|---|
.production | https://api.surt.com |
.sandbox | https://sandbox-api.surt.com |
Datenerfassungsoptionen
| Option | Standard | Beschreibung |
|---|---|---|
collectLocation | false | GPS-Koordinaten - erfordert NSLocationWhenInUseUsageDescription in der Info.plist |
collectWifiInfo | false | WiFi-Netzwerkdetails |
collectSimCardInfo | false | SIM-/Carrier-Informationen |
collectCameraInfo | false | Kameraanzahl/-info - erfordert NSCameraUsageDescription in der Info.plist |
Wenn collectLocation aktiviert ist, fordert das SDK den Benutzer beim ersten Aufruf von verify() automatisch zur Standortberechtigung auf. Wenn der Benutzer ablehnt, fährt das SDK ohne GPS-Daten fort - das Backend bewertet die Transaktion mit geringerer Sicherheit.
5. JWT von Ihrem Backend erzeugen
Vor jedem verify()-Aufruf muss Ihre App ein kurzlebiges GeolocationJwt von Ihrem eigenen Backend holen. Ihr Backend ruft POST /geolocation/preflight mit seinem serverseitigen API-Schlüssel und dem Transaktionskontext auf und gibt dann das JWT an die App zurück.
Beispiel-Backend-Aufruf (Ihr Server, nicht die App):
POST /geolocation/preflight
Authorization: Bearer sp_live_xxx
Content-Type: application/json
{
"customer_id": "user_abc123",
"transaction_type": "login",
"transaction_name": "User Login",
"name": "John Doe",
"email": "john@example.com"
}
Die Preflight-Antwort gibt das Token zurück, das Ihre App benötigt:
{
"data": {
"token": "<jwt>"
}
}
Ihre App holt das JWT von Ihrem eigenen Backend-Endpoint:
// In Ihrer App - rufen Sie Ihr EIGENES Backend auf, nicht Surt direkt
func fetchVerifyJwt() async throws -> String {
let response = try await URLSession.shared.data(
for: URLRequest(url: URL(string: "https://your-api.com/geolocation-jwt")!)
)
// JWT-String aus der Antwort Ihres Backends parsen und zurückgeben
let body = try JSONDecoder().decode(YourJwtResponse.self, from: response.0)
return body.jwt
}
Holen Sie immer ein frisches JWT unmittelbar vor dem Aufruf von verify(). Jedes JWT ist einmalig verwendbar; die Wiederverwendung eines JWT wird vom Backend abgelehnt.
6. Eine Transaktion verifizieren
Bei sicherheitssensiblen Momenten aufrufen (Login, Zahlung usw.). Holen Sie unmittelbar vor jedem Aufruf ein JWT von Ihrem Backend.
let jwt = try await fetchVerifyJwt()
let result = try await GuardianSDK.shared.verify(jwt: jwt)
if result.allowed {
// Mit der Transaktion fortfahren
} else {
// Abgelehnte Transaktion behandeln (result.riskLevel enthält Details)
}
Oder mit einem Completion-Handler (iOS 14+):
let jwt = try await fetchVerifyJwt()
GuardianSDK.shared.verify(jwt: jwt) { result in
switch result {
case .success(let verification):
if verification.allowed { /* fortfahren */ }
case .failure(let error):
// SurtError behandeln
}
}
Standortüberschreibung pro Aufruf:
// GPS für diesen Aufruf erzwingen, unabhängig vom Init-Standard
let result = try await GuardianSDK.shared.verify(
jwt: jwt,
collectLocation: true
)
7. Collect (Server-zu-Server, optional)
collect() gibt ein verschlüsseltes Payload zurück, das Ihr Backend an Surt weiterleitet. Während collect erfolgen keine direkten SDK-zu-Surt-Aufrufe.
// Ohne JWT - keine Surt-Netzwerkaufrufe, keine IP im Payload
let result = try await GuardianSDK.shared.collect()
// Mit JWT - löst die öffentliche IP des Geräts auf (GET /geolocation/client-ip),
// bettet sie in das Payload ein (best-effort)
let jwtForCollect = try await fetchCollectJwt()
let result = try await GuardianSDK.shared.collect(jwt: jwtForCollect)
// result.payload an Ihr Backend senden
Das JWT für collect() ist optional. Übergeben Sie es nur, um die öffentliche IP des Geräts einzubetten. Anders als bei verify() darf ein JWT für collect() wiederverwendet oder weggelassen werden - collect() benötigt kein JWT pro Aufruf und macht keinen Surt-Netzwerkaufruf, wenn kein JWT übergeben wird.
Verifizierungsergebnis
struct VerificationResult {
let allowed: Bool // Backend-Entscheidung - true = fortfahren
let riskLevel: RiskLevel // .low / .medium / .high / .blocked / .unknown
let sessionId: String // Transaktions-ID für Support-Referenz
let errors: [String]? // Backend-Fehlermeldungen, falls vorhanden
let timestamp: TimeInterval // Antwortzeitstempel
}
Transaktionstypen
Diese Werte werden von Ihrem Backend im Preflight-Feld transaction_type gesetzt. Sie werden in der App nicht an verify() übergeben.
| Typ | Anwendungsfall |
|---|---|
.login | Benutzeranmeldung |
.signUp | Neues Konto erstellen |
.deposit | Einzahlung |
.withdrawal | Auszahlung |
Fehlerbehebung
| Fehler | Ursache | Lösung |
|---|---|---|
| "Package resolution failed" | Token nicht konfiguriert oder abgelaufen | Prüfen Sie, ob ~/.netrc das richtige Token enthält |
.notInitialized | verify() wurde vor initialize() aufgerufen | Rufen Sie initialize() beim App-Start auf |
.invalidJwt | JWT fehlt, ist fehlerhaft oder wurde abgelehnt | Stellen Sie sicher, dass Ihr Backend pro Aufruf ein frisches JWT erzeugt |
.attestationFailed | Geräteintegritätsprüfung fehlgeschlagen - meist ein wiederverwendetes JWT | Verwenden Sie ein JWT niemals über mehrere verify()-Aufrufe hinweg wieder |