Pular para o conteúdo principal
Versão: Guardian v0.1.0

iOS

Integração nativa iOS para o Surt Guardian SDK, distribuída via Swift Package Manager. O SDK coleta sinais do dispositivo, executa verificações de integridade do dispositivo e retorna a decisão de risco do backend.

O SDK nunca guarda uma chave de API da Surt. Seu backend guarda a chave sp_live_* (somente no lado do servidor) e a troca por um JWT de curta duração que o app passa para verify().

Version

Este guia cobre o SDK v0.3.0, que também corrige a detecção de VPN.

Requisitos​

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

1. Autenticação​

Adicione seu token de acesso ao ~/.netrc para que o Xcode possa clonar o repositório privado:

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

Substitua <YOUR_TOKEN> pelo token fornecido pela Surt.

dica

Se o ~/.netrc não existir, crie-o. Certifique-se de que ele tenha permissões restritas:

chmod 600 ~/.netrc

2. Adicionar o Pacote​

  1. No Xcode, vá para File > Add Package Dependencies
  2. Insira a URL do repositório: https://github.com/surtTech/surt-guardian-sdk
  3. Defina a regra de versão como "Up to Next Minor" a partir de 0.3.0
  4. Clique em Add Package
  5. Selecione a biblioteca SurtGuardianSDK e adicione-a ao seu target

3. Configurar as Permissões do Info.plist​

Se você habilitar a coleta de localização, adicione esta chave ao seu Info.plist:

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

Se você habilitar a coleta de informações da câmera, adicione esta chave:

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

O SDK solicita automaticamente a permissão relevante durante a verificação quando a opção de coleta correspondente está habilitada. A caixa de diálogo do sistema aparece automaticamente - nenhum código adicional é necessário.

4. Inicializar o SDK​

Chame uma vez na inicialização do app, antes de qualquer outro método do SDK. Nenhuma chave de API é necessária no SDK - sua chave de API do lado do servidor nunca chega ao dispositivo.

AppDelegate.swift
import SurtGuardianSDK

// No seu AppDelegate ou no init do App:
GuardianSDK.initialize(
options: GuardianOptions(
environment: .production,
failurePolicy: .fail,
logLevel: .warn,
collectLocation: true // Habilita a coleta de GPS (opcional)
)
)

Ambientes​

AmbienteURL Base
.productionhttps://api.surt.com
.sandboxhttps://sandbox-api.surt.com

Opções de Coleta de Dados​

OpçãoPadrãoDescrição
collectLocationfalseCoordenadas GPS - requer NSLocationWhenInUseUsageDescription no Info.plist
collectWifiInfofalseDetalhes de rede WiFi
collectSimCardInfofalseInformações de SIM/operadora
collectCameraInfofalseContagem/informações da câmera - requer NSCameraUsageDescription no Info.plist

Quando collectLocation está habilitado, o SDK solicita automaticamente ao usuário a permissão de localização na primeira vez que verify() é chamado. Se o usuário negar, o SDK continua sem dados de GPS - o backend pontua a transação com menos confiança.

5. Gerar um JWT a Partir do Seu Backend​

Antes de cada chamada verify(), seu app deve obter um GeolocationJwt de curta duração do seu próprio backend. Seu backend chama POST /geolocation/preflight com sua chave de API do lado do servidor e o contexto da transação, e então retorna o JWT para o app.

Exemplo de chamada do backend (seu servidor, não o 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"
}

A resposta do preflight retorna o token que seu app precisa:

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

Seu app obtém o JWT a partir do endpoint do seu próprio backend:

Fetch JWT from your backend
// No seu app - chame o SEU PRÓPRIO backend, não a Surt diretamente
func fetchVerifyJwt() async throws -> String {
let response = try await URLSession.shared.data(
for: URLRequest(url: URL(string: "https://your-api.com/geolocation-jwt")!)
)
// Faça o parse e retorne a string do JWT da resposta do seu backend
let body = try JSONDecoder().decode(YourJwtResponse.self, from: response.0)
return body.jwt
}
Always fetch a fresh JWT per call

Sempre obtenha um JWT novo imediatamente antes de chamar verify(). Cada JWT é de uso único; reutilizar um JWT é rejeitado pelo backend.

6. Verificar uma Transação​

Chame em momentos sensíveis à segurança (login, pagamento, etc.). Obtenha um JWT do seu backend imediatamente antes de cada chamada.

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

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

if result.allowed {
// Prosseguir com a transação
} else {
// Tratar transação negada (result.riskLevel tem os detalhes)
}

Ou com um 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 { /* prosseguir */ }
case .failure(let error):
// Tratar SurtError
}
}

Substituição de localização por chamada:

Per-call location override
// Forçar GPS ligado para esta chamada, independentemente do padrão de inicialização
let result = try await GuardianSDK.shared.verify(
jwt: jwt,
collectLocation: true
)

7. Collect (Servidor para Servidor, Opcional)​

collect() retorna um payload criptografado para o seu backend encaminhar à Surt. Nenhuma chamada direta do SDK para a Surt acontece durante o collect.

Collect payload
// Sem JWT - nenhuma chamada de rede para a Surt, sem IP no payload
let result = try await GuardianSDK.shared.collect()

// Com JWT - resolve o IP público do dispositivo (GET /geolocation/client-ip),
// e o embute no payload (melhor esforço)
let jwtForCollect = try await fetchCollectJwt()
let result = try await GuardianSDK.shared.collect(jwt: jwtForCollect)

// Envie result.payload para o seu backend
JWT is optional for collect

O JWT para collect() é opcional. Passe-o apenas para embutir o IP público do dispositivo. Diferente de verify(), um JWT para collect() pode ser reutilizado ou omitido - collect() não exige um JWT por chamada e não faz nenhuma chamada de rede à Surt quando nenhum JWT é fornecido.

Resultado da Verificação​

VerificationResult
struct VerificationResult {
let allowed: Bool // Decisão do backend - true = prosseguir
let riskLevel: RiskLevel // .low / .medium / .high / .blocked / .unknown
let sessionId: String // ID da transação para referência de suporte
let errors: [String]? // Mensagens de erro do backend, se houver
let timestamp: TimeInterval // Timestamp da resposta
}

Tipos de Transação​

Estes valores são definidos pelo seu backend no campo transaction_type do preflight. Eles não são passados para verify() no app.

TipoCaso de uso
.loginLogin do usuário
.signUpCriação de nova conta
.depositAdição de fundos
.withdrawalSaque de fundos

Solução de Problemas​

ErroCausaSolução
"Package resolution failed"Token não configurado ou expiradoVerifique se o ~/.netrc tem o token correto
.notInitializedverify() chamado antes de initialize()Chame initialize() na inicialização do app
.invalidJwtJWT ausente, malformado ou rejeitadoGaranta que seu backend gere um JWT novo a cada chamada
.attestationFailedFalha na verificação de integridade do dispositivo - geralmente um JWT reutilizadoNunca reutilize um JWT entre chamadas verify()