Kimliğinizi Kanıtlayın, Gizliliğinizi Koruyun · Prove Your Identity, Protect Your Privacy
Android uygulamalarına VerifyBlind kimlik doğrulaması entegre etmek için resmi Android SDK.
[Partner Android App (SDK)] ← Geçici RSA-OAEP anahtar çifti burada üretilir
│
│ POST { public_key, validations, custom_data }
▼
[Partner'ın Backend Sunucusu] ← Aracı: X-API-Key ekler, validations'ı KENDİ ayarından koyar
│
│ POST /api/pop/generate
▼
[VerifyBlind API] ──► { nonce }
│
▼
[Partner Android App (SDK)]
│
│ https://app.verifyblind.com/request?nonce=...&pk_hash=...
▼
[VerifyBlind Mobil Uygulaması] ← Kullanıcı kimliğini doğrular
│
▼
[Partner Android App (SDK)] ← GET /api/pop/result/{nonce} ile sonucu sorar,
şifreli yanıtı geçici özel anahtarla cihazda çözer
API anahtarınız yalnızca backend'inizde durur; mobil uygulamada hiçbir gizli anahtar bulunmaz.
SDK, Maven Central'da yayımlanır. mavenCentral() deposu (Android projelerinde varsayılan olarak
açıktır) yeterlidir:
// app/build.gradle.kts
dependencies {
implementation("com.verifyblind:verifyblind-android:1.0.1")
}Gereksinimler: minSdk 24, Java 17.
import com.verifyblind.sdk.VerifyBlindAndroidSDK
import com.verifyblind.sdk.VerifyBlindConfig
import com.verifyblind.sdk.VerifyBlindException
val sdk = VerifyBlindAndroidSDK(
VerifyBlindConfig(
partnerBackendUrl = "https://partner.example.com/api/auth/",
generateEndpoint = "verifyblind-generate",
verifyblindAppLinkBase = "https://app.verifyblind.com/request"
)
)
viewModelScope.launch {
try {
val start = sdk.startAuthentication(
context = context,
validations = mapOf("age" to "18+", "user_id" to true) // İsteğe bağlı
)
// VerifyBlind uygulaması açılır. Kullanıcı uygulamanıza döndüğünde sonucu sorun:
val result = sdk.checkVerificationResult(start.nonce) // null = henüz bekliyor
// result["token"] → imzalı ham yanıt; KARAR İÇİN bunu sunucunuza gönderin (aşağıya bakın)
// result["validations"] → { age: true, user_id: "...", nsbd_id: "...", doc_id: "..." }
} catch (e: VerifyBlindException) {
// e.code: NETWORK_ERROR | PARTNER_BACKEND_ERROR | INVALID_RESPONSE
// APP_LINK_FAILED | USER_CANCELLED (e.cancelReason ile)
}
}Sunucuda doğrulama (önemli): Engelleme, ödül ya da yaş kapısı gibi bir kararı telefondaki sonuca göre
vermeyin; uygulama kandırılabilir. result["token"]'ı kendi sunucunuza gönderin. Sunucunuz web
entegrasyonundakiyle aynı doğrulamayı yapar: imzayı (RSA-PSS, enclave anahtarı) doğrular, nonce'u
tek seferlik tüketir ve sonucu nonce ile sakladığı koşula göre okur. Ayrıntılar:
https://verifyblind.com/ai-integration.md (desen A, 4. adım).
checkVerificationResult, sonuç hazır değilken null döner; birkaç saniye arayla tekrar çağırın. Kullanıcı
işlemi iptal ettiyse USER_CANCELLED koduyla VerifyBlindException fırlatır; iptal nedeni
cancelReason alanındadır (no_card_registered, user_declined, fingerprint_failed, session_expired,
user_cancelled). Nonce 15 dakika geçerlidir.
VerifyBlind'ı kendi mobil uygulamanızdan açtığınızda returnUrl verirseniz, akış bittiğinde (başarı ya da
iptal) VerifyBlind kullanıcıyı uygulamanıza geri getirir.
val start = sdk.startAuthentication(
context = context,
validations = mapOf("user_id" to true),
returnUrl = "verifyblinddemo://callback" // uygulamanızın özel şeması
)İşlem bitince VerifyBlind verifyblinddemo://callback?nonce={nonce}&status=success (ya da
status=cancelled) adresini açar ve uygulamanız öne gelir. Sonucu okumak için checkVerificationResult
ile sormaya devam edin.
Gereken iki adım:
- Şemayı VerifyBlind Partner Portalı → Ayarlar → Uygulamaya Dönüş Şeması bölümüne kaydedin
(ör.
verifyblinddemo). VerifyBlind yalnızca kayıtlı şemayla eşleşen dönüş adreslerini açar; alan boşsa uygulamaya dönüş kapalıdır. - Şemayı
AndroidManifest.xmliçinde tanımlayın:
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="verifyblinddemo" android:host="callback" />
</intent-filter>
</activity>QR (cihazlar arası) akışlarında
returnUrlkullanılmaz — dönülecek bir uygulama yoktur.
Partner backend'inizde şu endpoint'i oluşturun:
// Node.js örneği — kimlik doğrulama X-API-Key header'ı ile.
// SDK'nın gönderdiği gövde: { public_key, validations?, custom_data? }
// Ne sorulacağına SUNUCUNUZ karar verir: uygulamanın gönderdiği validations kullanılmaz.
// (İstek değiştirilebilir — "18+" yerine "1+" soran biri de imzalı age: true alır.)
const VALIDATIONS = { age: '18+', user_id: true };
app.post('/api/auth/verifyblind-generate', async (req, res) => {
const { public_key, custom_data } = req.body;
const response = await fetch('https://api.verifyblind.com/api/pop/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.VERIFYBLIND_API_KEY },
body: JSON.stringify({ public_key, custom_data, validations: VALIDATIONS }),
});
// Durum kodunu koru; relay { nonce } döner. pk_hash'i SDK kendisi hesaplar.
// Sonucu sunucuda kontrol edecekseniz nonce'u sorduğunuz koşulla birlikte saklayın (TTL 960 sn).
res.status(response.status).json(await response.json());
});Not: SDK,
additional_datayerinecustom_dataalanını gönderir; relay/api/pop/generateikisini de kabul eder.public_keyvecustom_dataaynen iletilir;validationsise her zaman sunucunuzun ayarından gelir. İmzalı sonuçtakivalidations.age_condition(yeni enclave sürümlerinde) sorulan koşulu gösterir; sizin sorduğunuz koşula eşit olmalıdır.
validations içinde user_id: true isterseniz, çözülen yanıtta üç kod birden döner — üçünü de saklayın:
| Alan | Anlam |
|---|---|
user_id |
Ulusal-no bazlı kimlik. Partner'a özel HMAC. Türetilemezse alan yanıtta yer almaz. |
nsbd_id |
Biyografik kişi kodu; ad ve doğum tarihi değişmediği sürece kişinin kartları arasında sabit. Olasılıksal ipucu — tek başına sert dedup kararı vermeyin. Kayabileceği durumlar: isim değişikliği (ör. evlilik) ve uzun isimlerin farklı belgelerde farklı kırpılması (MRZ kırpması ICAO 9303'te ihraççı takdirindedir). Sert karar için doc_id kullanın. |
doc_id |
Belge kodu; aynı doc_id = aynı fiziksel belge = aynı kişi (sert sinyal). |
Üçü de partner'a özeldir (başka partner ile eşleştirilemez) ve TCKN'ye döndürülemez. Demo kartla yapılan
doğrulamalarda validations.is_test: true döner ve kodlar TEST_ önekiyle gelir.
| Parametre | Zorunlu | Varsayılan | Açıklama |
|---|---|---|---|
partnerBackendUrl |
✅ | — | Backend'inizdeki aracı (proxy) endpoint'in taban adresi |
generateEndpoint |
❌ | . |
partnerBackendUrl'e eklenen göreli yol |
verifyblindAppLinkBase |
✅ | — | VerifyBlind App Link adresi: https://app.verifyblind.com/request |
verifyblindApiUrl |
❌ | https://api.verifyblind.com |
Sonucun sorgulandığı VerifyBlind API adresi |
certificatePins |
❌ | null |
İsteğe bağlı sertifika sabitleme listesi (OkHttp pin biçimi) |
skipSecurityChecks |
❌ | false |
Yalnızca geliştirme ortamı içindir; sertifika sabitlemeyi kapatır |
- API anahtarı asla mobil uygulamada olmamalıdır. Anahtar yalnızca backend'inizdeki aracı endpoint'te kullanılır.
- Sonuç, cihazda üretilen geçici anahtarla şifreli gelir ve yalnızca aynı
VerifyBlindAndroidSDKörneği çözebilir. - SDK, çözülmüş sonucu uygulamanıza verir; enclave imzasını ve kanıtını ayrıca döndürmez. Sonucun sunucunuzda imzasıyla doğrulanması gereken senaryolarda web entegrasyonunu kullanın (bkz. Geliştirici Dokümantasyonu).
skipSecurityChecks=trueyalnızca geliştirme/test ortamı içindir. Üretimde kullanmayınız.
| Sürüm | Açıklama |
|---|---|
| 1.0.1 | Sonuçta token alanı: enclave'in imzaladığı ham yanıt (web widget token'ıyla aynı biçim); sunucuda doğrulamak için |
| 1.0.0 | İlk sürüm: geçici anahtarlı (PoP) akış, validations, App Link ve uygulamaya dönüş desteği |
The official Android SDK for integrating VerifyBlind identity verification into Android apps.
[Partner Android App (SDK)] ← A temporary RSA-OAEP key pair is generated here
│
│ POST { public_key, validations, custom_data }
▼
[Partner Backend Server] ← Proxy: adds X-API-Key, sets validations from ITS OWN config
│
│ POST /api/pop/generate
▼
[VerifyBlind API] ──► { nonce }
│
▼
[Partner Android App (SDK)]
│
│ https://app.verifyblind.com/request?nonce=...&pk_hash=...
▼
[VerifyBlind Mobile App] ← The user verifies their identity
│
▼
[Partner Android App (SDK)] ← Polls GET /api/pop/result/{nonce} and decrypts the
encrypted response on the device with the temporary key
Your API key stays only on your backend; there is no secret in the mobile app.
The SDK is published on Maven Central. The mavenCentral() repository (enabled by default in Android
projects) is all you need:
// app/build.gradle.kts
dependencies {
implementation("com.verifyblind:verifyblind-android:1.0.1")
}Requirements: minSdk 24, Java 17.
import com.verifyblind.sdk.VerifyBlindAndroidSDK
import com.verifyblind.sdk.VerifyBlindConfig
import com.verifyblind.sdk.VerifyBlindException
val sdk = VerifyBlindAndroidSDK(
VerifyBlindConfig(
partnerBackendUrl = "https://partner.example.com/api/auth/",
generateEndpoint = "verifyblind-generate",
verifyblindAppLinkBase = "https://app.verifyblind.com/request"
)
)
viewModelScope.launch {
try {
val start = sdk.startAuthentication(
context = context,
validations = mapOf("age" to "18+", "user_id" to true) // Optional
)
// The VerifyBlind app opens. When the user returns to your app, ask for the result:
val result = sdk.checkVerificationResult(start.nonce) // null = still pending
// result["token"] → the signed raw answer; send THIS to your server for any decision (see below)
// result["validations"] → { age: true, user_id: "...", nsbd_id: "...", doc_id: "..." }
} catch (e: VerifyBlindException) {
// e.code: NETWORK_ERROR | PARTNER_BACKEND_ERROR | INVALID_RESPONSE
// APP_LINK_FAILED | USER_CANCELLED (with e.cancelReason)
}
}Verify on your server (important): do not make a decision (blocking, rewards, age gates) from the
result on the phone; an app can be tampered with. Send result["token"] to your own server. It runs the
same check as a web integration: verify the signature (RSA-PSS, enclave key), consume the nonce once,
and read the result against the condition stored with the nonce. Details:
https://verifyblind.com/ai-integration.md (pattern A, step 4).
checkVerificationResult returns null while the result is not ready; call it again every few seconds.
If the user cancelled, it throws a VerifyBlindException with code USER_CANCELLED; the reason is in
cancelReason (no_card_registered, user_declined, fingerprint_failed, session_expired,
user_cancelled). The nonce is valid for 15 minutes.
When you launch VerifyBlind from your own mobile app, pass a returnUrl so VerifyBlind brings the user
back to your app when the flow ends (success or cancel) — instead of leaving them inside VerifyBlind.
val start = sdk.startAuthentication(
context = context,
validations = mapOf("user_id" to true),
returnUrl = "verifyblinddemo://callback" // your app's custom scheme
)When done, VerifyBlind opens verifyblinddemo://callback?nonce={nonce}&status=success (or
status=cancelled), which foregrounds your app. Keep calling checkVerificationResult to read the result.
Two required steps:
- Register the scheme in the VerifyBlind Partner Portal → Settings → App Return Scheme
(e.g.
verifyblinddemo). VerifyBlind only opens a return URL whose scheme matches your registered value (fail-closed — prevents open-redirect). Leaving it empty disables app return. - Declare the scheme in your
AndroidManifest.xmlso the callback opens your app:
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="verifyblinddemo" android:host="callback" />
</intent-filter>
</activity>QR (cross-device) flows ignore
returnUrl— there is no caller app to return to.
Create this endpoint on your partner backend:
// Node.js example — authentication is the X-API-Key header.
// Body sent by the SDK: { public_key, validations?, custom_data? }
// YOUR SERVER decides what is asked: the validations sent by the app are not used.
// (The request can be edited — someone who asks "1+" instead of "18+" also gets a signed age: true.)
const VALIDATIONS = { age: '18+', user_id: true };
app.post('/api/auth/verifyblind-generate', async (req, res) => {
const { public_key, custom_data } = req.body;
const response = await fetch('https://api.verifyblind.com/api/pop/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.VERIFYBLIND_API_KEY },
body: JSON.stringify({ public_key, custom_data, validations: VALIDATIONS }),
});
// Preserve the status code; the relay returns { nonce }. The SDK computes pk_hash itself.
// If your server checks the result later, store the nonce with the condition you asked (TTL 960 s).
res.status(response.status).json(await response.json());
});Note: the SDK sends
custom_datainstead ofadditional_data; the relay's/api/pop/generateaccepts both. Forwardpublic_keyandcustom_dataunchanged;validationsalways come from your server configuration. In the signed result,validations.age_condition(newer enclave releases) states the condition that was asked; it must equal the condition you asked.
If you request user_id: true in validations, the decrypted response returns three codes at once —
store all three:
| Field | Meaning |
|---|---|
user_id |
National-ID-based identity. Partner-specific HMAC. Left out of the response if it cannot be derived. |
nsbd_id |
Biographic person code; stable across a person's cards as long as name and date of birth do not change. A probabilistic hint — do not make a hard dedup decision on it alone. It can drift on a name change (e.g. marriage) or when a long name is truncated differently on different documents (MRZ truncation is at the issuer's discretion under ICAO 9303). Use doc_id for hard decisions. |
doc_id |
Document code; same doc_id = same physical document = same person (hard signal). |
All three are partner-specific (cannot be linked across partners) and cannot be reversed to the national ID
number. Verifications made with a demo card return validations.is_test: true, and the codes carry a
TEST_ prefix.
| Parameter | Required | Default | Description |
|---|---|---|---|
partnerBackendUrl |
✅ | — | Base URL of the proxy endpoint on your backend |
generateEndpoint |
❌ | . |
Relative path appended to partnerBackendUrl |
verifyblindAppLinkBase |
✅ | — | VerifyBlind App Link: https://app.verifyblind.com/request |
verifyblindApiUrl |
❌ | https://api.verifyblind.com |
VerifyBlind API the result is polled from |
certificatePins |
❌ | null |
Optional certificate pin list (OkHttp pin format) |
skipSecurityChecks |
❌ | false |
For development only; disables certificate pinning |
- The API key must never be in the mobile app. It is used only in the proxy endpoint on your backend.
- The result arrives encrypted with the temporary key generated on the device, and only the same
VerifyBlindAndroidSDKinstance can decrypt it. - The SDK gives your app the decrypted result; it does not also return the enclave signature and proof. Where your server must verify the result by its signature, use the web integration (see the Developer Documentation).
skipSecurityChecks=trueis for development/testing only. Do not use it in production.
| Version | Description |
|---|---|
| 1.0.1 | token in the result: the enclave-signed raw answer (same format as the web widget token), for server-side verification |
| 1.0.0 | First release: temporary-key (PoP) flow, validations, App Link and app-return support |
Apache License 2.0 — bkz. / see LICENSE.