Saltar al contenido principal
Version: Guardian v0.1.0

iOS

Integración nativa de iOS para el SDK de Surt Guardian, distribuido mediante Swift Package Manager. El SDK recopila señales del dispositivo, ejecuta verificaciones de integridad del dispositivo y devuelve la decisión de riesgo del backend.

El SDK nunca maneja una clave de API de Surt. Tu backend maneja la clave sp_live_* (solo del lado del servidor) y la intercambia por un JWT de corta duración que la app pasa a verify().

Version

Esta guía cubre el SDK v0.3.0, que también corrige la detección de VPN.

Requisitos​

  • iOS 14.0+
  • Xcode 14+
  • Swift 5.7+

1. Autenticación​

Agrega tu token de acceso a ~/.netrc para que Xcode pueda clonar el repositorio privado:

~/.netrc
machine github.com
login surt-customer
password <YOUR_TOKEN>

Reemplaza <YOUR_TOKEN> con el token proporcionado por Surt.

tip

Si ~/.netrc no existe, créalo. Asegúrate de que tenga permisos restringidos:

chmod 600 ~/.netrc

2. Agregar el paquete​

  1. En Xcode, ve a File > Add Package Dependencies
  2. Ingresa la URL del repositorio: https://github.com/surtTech/surt-guardian-sdk
  3. Establece la regla de versión en "Up to Next Minor" desde 0.3.0
  4. Haz clic en Add Package
  5. Selecciona la librería SurtGuardianSDK y agrégala a tu target

3. Configurar permisos en Info.plist​

Si habilitas la recopilación de ubicación, agrega esta clave a tu Info.plist:

Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location helps verify your identity and protect your account.</string>

Si habilitas la recopilación de información de cámara, agrega esta clave:

Info.plist
<key>NSCameraUsageDescription</key>
<string>Camera access helps verify device authenticity.</string>

El SDK solicita automáticamente el permiso correspondiente durante la verificación cuando la opción de recopilación coincidente está habilitada. El cuadro de diálogo del sistema aparece automáticamente; no se necesita código adicional.

4. Inicializar el SDK​

Llama una sola vez al arrancar la app, antes de cualquier otro método del SDK. No se requiere ninguna clave de API en el SDK; tu clave de API del lado del servidor nunca se entrega al dispositivo.

AppDelegate.swift
import SurtGuardianSDK

// In your AppDelegate or App init:
GuardianSDK.initialize(
options: GuardianOptions(
environment: .production,
failurePolicy: .fail,
logLevel: .warn,
collectLocation: true // Enable GPS collection (optional)
)
)

Entornos​

EntornoURL base
.productionhttps://api.surt.com
.sandboxhttps://sandbox-api.surt.com

Opciones de recopilación de datos​

OpciónPredeterminadoDescripción
collectLocationfalseCoordenadas GPS - requiere NSLocationWhenInUseUsageDescription en Info.plist
collectWifiInfofalseDetalles de red WiFi
collectSimCardInfofalseInformación de SIM/operador
collectCameraInfofalseCantidad/información de cámara - requiere NSCameraUsageDescription en Info.plist

Cuando collectLocation está habilitado, el SDK solicita automáticamente al usuario el permiso de ubicación la primera vez que se llama a verify(). Si el usuario lo deniega, el SDK continúa sin datos GPS; el backend evalúa la transacción con menos confianza.

5. Generar un JWT desde tu backend​

Antes de cada llamada a verify(), tu app debe obtener un GeolocationJwt de corta duración desde tu propio backend. Tu backend llama a POST /geolocation/preflight con su clave de API del lado del servidor y el contexto de la transacción, y luego devuelve el JWT a la app.

Ejemplo de llamada del backend (tu servidor, no la app):

Backend preflight request
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"
}

La respuesta del preflight devuelve el token que tu app necesita:

Preflight response
{
"data": {
"token": "<jwt>"
}
}

Tu app obtiene el JWT desde tu propio endpoint del backend:

Fetch JWT from your backend
// In your app - call your OWN backend, not Surt directly
func fetchVerifyJwt() async throws -> String {
let response = try await URLSession.shared.data(
for: URLRequest(url: URL(string: "https://your-api.com/geolocation-jwt")!)
)
// Parse and return the JWT string from your backend response
let body = try JSONDecoder().decode(YourJwtResponse.self, from: response.0)
return body.jwt
}
Always fetch a fresh JWT per call

Obtén siempre un JWT fresco justo antes de llamar a verify(). Cada JWT es de un solo uso; reutilizar uno es rechazado por el backend.

6. Verificar una transacción​

Llama en momentos sensibles de seguridad (inicio de sesión, pago, etc.). Obtén un JWT de tu backend justo antes de cada llamada.

async/await (iOS 15+)
let jwt = try await fetchVerifyJwt()

let result = try await GuardianSDK.shared.verify(jwt: jwt)

if result.allowed {
// Proceed with the transaction
} else {
// Handle denied transaction (result.riskLevel has details)
}

O con un completion handler (iOS 14+):

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 { /* proceed */ }
case .failure(let error):
// Handle SurtError
}
}

Anulación de ubicación por llamada:

Per-call location override
// Force GPS on for this call, regardless of init default
let result = try await GuardianSDK.shared.verify(
jwt: jwt,
collectLocation: true
)

7. Collect (Servidor a servidor, opcional)​

collect() devuelve un payload cifrado para que tu backend lo reenvíe a Surt. No ocurren llamadas directas del SDK a Surt durante collect.

Collect payload
// Without JWT - no Surt network calls, no IP in payload
let result = try await GuardianSDK.shared.collect()

// With JWT - resolves device public IP (GET /geolocation/client-ip),
// embeds it in payload (best-effort)
let jwtForCollect = try await fetchCollectJwt()
let result = try await GuardianSDK.shared.collect(jwt: jwtForCollect)

// Send result.payload to your backend
JWT is optional for collect

El JWT para collect() es opcional. Pásalo solo para incrustar la IP pública del dispositivo. A diferencia de verify(), un JWT para collect() puede reutilizarse u omitirse: collect() genera su propio nonce internamente y no realiza ninguna llamada de red a Surt cuando no se proporciona un JWT.

Resultado de la verificación​

VerificationResult
struct VerificationResult {
let allowed: Bool // Backend decision - true = proceed
let riskLevel: RiskLevel // .low / .medium / .high / .blocked / .unknown
let sessionId: String // Transaction ID for support reference
let errors: [String]? // Backend error messages, if any
let timestamp: TimeInterval // Response timestamp
}

Tipos de transacción​

Estos valores los establece tu backend en el campo transaction_type del preflight. No se pasan a verify() en la app.

TipoCaso de uso
.loginInicio de sesión del usuario
.signUpCreación de cuenta nueva
.depositAgregar fondos
.withdrawalRetirar fondos

Solución de problemas​

ErrorCausaSolución
"Package resolution failed"Token no configurado o expiradoVerifica que ~/.netrc tenga el token correcto
.notInitializedverify() llamado antes de initialize()Llama a initialize() al arrancar la app
.invalidJwtJWT faltante, malformado o rechazadoAsegúrate de que tu backend genere un JWT fresco por cada llamada
.attestationFailedFalló una verificación de integridad del dispositivo, normalmente un JWT reutilizadoNunca reutilices un JWT entre llamadas a verify()