Android
Native Android-Integration für das Surt Guardian SDK, vertrieben über Maven (GitHub Packages). 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
- Android SDK 24+ (Android 7.0+)
- Kotlin 1.9+
- Gradle 8+
- Java 17
1. Authentifizierung
Fügen Sie Ihr Zugriffstoken zur gradle.properties Ihres Projekts hinzu (oder zu ~/.gradle/gradle.properties für alle Projekte):
SURT_GITHUB_TOKEN=<YOUR_TOKEN>
Ersetzen Sie <YOUR_TOKEN> durch das von Surt bereitgestellte Token.
Schreiben Sie gradle.properties mit Token nicht in die Versionsverwaltung. Fügen Sie es zu .gitignore hinzu oder verwenden Sie stattdessen ~/.gradle/gradle.properties.
2. Repository hinzufügen
Fügen Sie in der settings.gradle (oder settings.gradle.kts) Ihres Projekts das Surt-Maven-Repository hinzu:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/surtTech/surt-guardian-sdk")
credentials {
username = "surt-customer"
password = settings.ext.find("SURT_GITHUB_TOKEN")
?: System.getenv("SURT_GITHUB_TOKEN") ?: ""
}
}
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/surtTech/surt-guardian-sdk")
credentials {
username = "surt-customer"
password = providers.gradleProperty("SURT_GITHUB_TOKEN").orNull
?: System.getenv("SURT_GITHUB_TOKEN")
}
}
}
}
3. Abhängigkeit hinzufügen
In der build.gradle (oder build.gradle.kts) Ihrer App:
dependencies {
implementation 'com.surt.guardian:securitysdk:0.3.0'
}
dependencies {
implementation("com.surt.guardian:securitysdk:0.3.0")
}
Core Library Desugaring
Wenn Ihre App minSdk < 26 als Ziel hat, stellen Sie sicher, dass Core Library Desugaring in der build.gradle Ihrer App aktiviert ist:
android {
compileOptions {
coreLibraryDesugaringEnabled true
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
}
4. AndroidManifest-Berechtigungen
Wenn Sie die Standorterfassung aktivieren, fügen Sie diese Berechtigungen zu Ihrer AndroidManifest.xml hinzu:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Wenn Sie die SIM-Karten-Informationserfassung aktivieren, fügen Sie diese Berechtigung hinzu:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Das SDK fordert die Standortberechtigung während der Verifizierung automatisch an, wenn collectLocation aktiviert ist. Der standardmäßige Android-Berechtigungsdialog erscheint automatisch - es ist kein zusätzlicher Code erforderlich.
5. SDK initialisieren
Einmal beim App-Start aufrufen (typischerweise in Ihrer Application-Klasse). Im SDK ist kein API-Schlüssel erforderlich - Ihr serverseitiger API-Schlüssel gelangt niemals auf das Gerät.
import android.app.Application
import com.surt.guardian.GuardianSDK
import com.surt.guardian.core.Environment
import com.surt.guardian.core.FailurePolicy
import com.surt.guardian.core.GuardianOptions
import com.surt.guardian.utils.Logger
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
GuardianSDK.initialize(
context = this,
options = GuardianOptions(
environment = Environment.Production,
failurePolicy = FailurePolicy.Fail,
logLevel = Logger.Level.WARN,
collectLocation = true // GPS-Erfassung aktivieren (optional)
)
)
}
}
Umgebungen
| Umgebung | Basis-URL |
|---|---|
Environment.Production | https://api.surt.com |
Environment.Sandbox | https://sandbox-api.surt.com |
Datenerfassungsoptionen
| Option | Standard | Beschreibung |
|---|---|---|
collectLocation | false | GPS-Koordinaten - erfordert Standortberechtigungen im Manifest |
collectWifiInfo | false | WiFi-Netzwerkdetails |
collectSimCardInfo | false | SIM-/Carrier-Informationen - erfordert READ_PHONE_STATE-Berechtigung |
collectCameraInfo | false | Kameraanzahl/-info |
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.
6. Activity-Referenz (für automatische Berechtigungsanfragen)
Damit das SDK den Berechtigungsdialog anzeigen kann, benötigt es eine Referenz auf die aktuelle Activity. Rufen Sie setActivity() im Lebenszyklus Ihrer Activity auf:
override fun onResume() {
super.onResume()
GuardianSDK.getInstance().setActivity(this)
}
override fun onPause() {
super.onPause()
GuardianSDK.getInstance().setActivity(null)
}
Wenn Sie setActivity() nicht aufrufen, funktioniert das SDK weiterhin, kann jedoch keine Berechtigungen automatisch anfordern. Sie müssten die Berechtigungen dann selbst anfordern, bevor Sie verify() aufrufen.
7. 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:
suspend fun fetchVerifyJwt(): String {
// Rufen Sie Ihren EIGENEN Backend-Endpoint auf, nicht Surt direkt
val response = httpClient.post("https://your-api.com/geolocation-jwt")
return response.body<YourJwtResponse>().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.
8. Eine Transaktion verifizieren
Bei sicherheitssensiblen Momenten aufrufen (Login, Zahlung usw.). Holen Sie unmittelbar vor jedem Aufruf ein JWT von Ihrem Backend.
val jwt = fetchVerifyJwt()
val result = GuardianSDK.getInstance().verifySuspend(jwt = jwt)
if (result.allowed) {
// Mit der Transaktion fortfahren
} else {
// Abgelehnte Transaktion behandeln (result.riskLevel enthält Details)
}
Oder mit einem Callback:
val jwt = fetchVerifyJwt()
GuardianSDK.getInstance().verify(jwt = jwt) { result ->
result.onSuccess { verification ->
if (verification.allowed) { /* fortfahren */ }
}
result.onFailure { error ->
// SurtError behandeln
}
}
Standortüberschreibung pro Aufruf:
// GPS für diesen Aufruf erzwingen, unabhängig vom Init-Standard
val result = GuardianSDK.getInstance().verifySuspend(
jwt = jwt,
collectLocation = true
)
9. 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
val result = GuardianSDK.getInstance().collectSuspend()
// Mit JWT - löst die öffentliche IP des Geräts auf (GET /geolocation/client-ip),
// bettet sie in das Payload ein (best-effort)
val jwtForCollect = fetchCollectJwt()
val result = GuardianSDK.getInstance().collectSuspend(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
data class VerificationResult(
val allowed: Boolean, // Backend-Entscheidung - true = fortfahren
val riskLevel: RiskLevel, // LOW / MEDIUM / HIGH / BLOCKED / UNKNOWN
val sessionId: String, // Transaktions-ID für Support-Referenz
val errors: List<String>?, // Backend-Fehlermeldungen, falls vorhanden
val timestamp: Long // Antwortzeitstempel (ms)
)
Transaktionstypen
Diese Werte werden von Ihrem Backend im Preflight-Feld transaction_type gesetzt. Sie werden in der App nicht an verify() übergeben.
| Typ | Anwendungsfall |
|---|---|
TransactionType.LOGIN | Benutzeranmeldung |
TransactionType.SIGN_UP | Neues Konto erstellen |
TransactionType.DEPOSIT | Einzahlung |
TransactionType.WITHDRAWAL | Auszahlung |
Fehlerbehebung
| Fehler | Ursache | Lösung |
|---|---|---|
| "Could not resolve com.surt.guardian:securitysdk" | Token nicht konfiguriert oder Repo nicht hinzugefügt | Prüfen Sie gradle.properties und settings.gradle |
| "401 Unauthorized" von maven.pkg.github.com | Token abgelaufen oder ungültig | Holen Sie ein neues Token von Surt |
.notInitialized | verify() wurde vor initialize() aufgerufen | Rufen Sie initialize() in Application.onCreate() 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 |