Biometric authentication iOS uygulamalarında çok temel bir feature oldu. Face ID ve Touch ID ile uygulamayı kilitlemek, hassas işlemleri onaylatmak, biometric-backed keychain kullanmak. Framework: LocalAuthentication.
5 uygulamamda biometric entegrasyonu yaptım. Bir bankacılık app’i, bir finans tracking’i, bir medical record app’i, iki privacy-sensitive productivity app’i. Her birinde biraz farklı pattern gerekti.
Temel API
import LocalAuthentication
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
print("Biometric not available: \(error?.localizedDescription ?? "")")
return
}
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Uygulamaya erişmek için") { success, error in
DispatchQueue.main.async {
if success {
// Auth success
} else {
// Auth failed
}
}
}4 satır. Ama doğru kullanım detaylarda.
Policy seçimi
Iki ana policy:
.deviceOwnerAuthenticationWithBiometrics: sadece Face ID / Touch ID. Biometric başarısız olursa kullanıcı başka şey deneyemiyor.
.deviceOwnerAuthentication: biometric + fallback to passcode. Biometric başarısız olursa device passcode’u sorar.
Ben genelde .deviceOwnerAuthentication tercih ediyorum. Kullanıcı Face ID ile kabul edilmeyen bir durumda (maske, karanlık, güneş gözlüğü) passcode ile devam edebiliyor. Friction azalıyor.
İstisna: bazı finansal işlem confirmation’larında “only biometric” disipline uygun olabiliyor. Passcode somebody-else-knows risk.
localizedReason: kullanıcıya ne söylüyorsunuz
Face ID prompt’unda görünen metin. Spesifik ve aksiyon-odaklı olmalı.
Kötü: “Authentication”
İyi: “Finansal bilgilere erişmek için Face ID”
Kullanıcı “niye biometric istiyor?” sorusunu 1 saniyede cevaplayabilmeli. Belirsiz mesaj güvensizlik yaratıyor.
TR localizasyon:
context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: NSLocalizedString(
"biometric.reason.vault",
value: "Kasaya erişmek için",
comment: ""
)
) { ... }Biometry type detection
Cihaz Face ID mı Touch ID mi var? Farklı metin göstermek için:
let context = LAContext()
var error: NSError?
context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
switch context.biometryType {
case .faceID:
buttonTitle = "Face ID ile gir"
case .touchID:
buttonTitle = "Touch ID ile gir"
case .opticID:
buttonTitle = "Optic ID ile gir" // Vision Pro
case .none:
buttonTitle = "Şifre ile gir"
@unknown default:
buttonTitle = "Güvenli giriş"
}biometryType check’inden önce canEvaluatePolicy çağrılmalı, yoksa type .none döner.
Icon seçimi: SF Symbols faceid vs touchid kullanın. Kullanıcı ikonu görünce ne olacağını anlar.
Info.plist permission
iOS 11.3+ Face ID için permission gerekli. Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>Uygulamanıza Face ID ile hızlı ve güvenli giriş yapabilirsiniz.</string>Bu string yoksa Face ID prompt’u hiç çıkmıyor, app crash ediyor review’da. Touch ID için ayrı permission yok.
LAError handling
Evaluation sırasında çıkabilecek hata tipleri:
context.evaluatePolicy(...) { success, error in
if let error = error as? LAError {
switch error.code {
case .userCancel:
// Kullanıcı Cancel'a tıkladı, sessizce geç
case .userFallback:
// Kullanıcı "Şifre Kullan" dedi, passcode akışına yönlendir
case .biometryNotAvailable:
// Cihazda biometric yok veya kapalı
showError("Face ID mevcut değil")
case .biometryNotEnrolled:
// Biometric var ama enroll edilmemiş
showError("Face ID kurulu değil. Ayarlar'dan ekleyin.")
case .biometryLockout:
// 5 başarısız deneme, passcode gerekiyor
showError("Face ID geçici olarak kilitli. Telefon şifresi gerekiyor.")
case .passcodeNotSet:
// Cihazda passcode yok
showError("Telefon şifresi tanımlı değil.")
case .systemCancel:
// System call'ı iptal etti (incoming call, vs)
case .invalidContext:
// Context invalid
default:
showError("Kimlik doğrulanamadı.")
}
}
}Her error type için farklı kullanıcı mesajı. Generic “auth failed” kullanıcıyı frustrate ediyor.
Biometric-backed Keychain
Biometric auth’un asıl gücü keychain’i biometric’le korumak. Kullanıcının password, token, secret key’i sadece biometric auth sonrası okunabilir oluyor.
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "com.example.vault",
kSecAttrAccount as String: "user_token",
kSecValueData as String: token.data(using: .utf8)!,
kSecAttrAccessControl as String: access
]
SecItemAdd(query as CFDictionary, nil).biometryCurrentSet flag’i: “sadece şu anki biometric set ile eşleşen kullanıcı okuyabilir”. Kullanıcı Settings’ten yeni bir parmak veya yeni bir face ekler ise eski keychain item invalidate oluyor.
.biometryAny alternative: herhangi bir biometric eşleşmesi (mevcut veya sonradan eklenen). Security-sensitive app’ler için .biometryCurrentSet daha sıkı.
Retry policy
Biometric başarısız olunca ne yapmalı? Otomatik retry etmeyin. Kullanıcı butona tıklayıp tekrar denesin.
Biometric auth failed
→ "Tekrar dene" butonu göster
→ kullanıcı tıklayınca evaluatePolicy tekrar çağrılıyorAutomatic retry flashing Face ID dialog’u spam ediyor, kullanıcı agresif hissediyor.
App background’a girince
App background’a girince biometric lock’u yeniden gerektirmek isteyebilirsiniz. Yaygın pattern:
func applicationWillResignActive(_ application: UIApplication) {
lockStartTime = Date()
}
func applicationDidBecomeActive(_ application: UIApplication) {
if let startTime = lockStartTime,
Date().timeIntervalSince(startTime) > 60 {
// 60 saniyeden fazla background, lock gerekli
showBiometricLock()
}
lockStartTime = nil
}Kullanıcı başka app’e geçti 10 saniye sonra döndü ise lock açılması gereksiz. 60 saniye iyi eşik. Sensitive app’lerde 30 saniye, ultra-sensitive’lerde her background’da lock.
Two-factor: biometric + something
Biometric alone sufficient bazı durumlar için değil. Yüksek tutarlı işlemlerde ikinci faktör eklemek security best practice.
Pattern:
– Düşük risk: biometric yeterli
– Orta risk: biometric + PIN
– Yüksek risk: biometric + SMS OTP veya time-based OTP
Biometric replay attack’a karşı korumalı değil (fotoğraf ile Face ID atlatmak nadir ama mümkün). Yüksek işlemlerde sadece biometric’e güvenmeyin.
Test disiplini
Simulator’da Face ID simulate edilebiliyor (Features → Face ID → Matching Face/Non-matching Face). Touch ID de simulate ediliyor.
Bunlar hızlı development için iyi. Final test real device’da şart – edge case’ler (maskeli Face ID, parmak ıslak Touch ID) simulator’da emule edilmiyor.
Kullanıcı’ya opt-in imkan
Biometric zorunlu yapmak yerine opt-in sunmak daha iyi UX. İlk app açılışında “Her giriste Face ID sormamızı ister misiniz?” sorusu, yes ise keychain’e biometric-backed kaydetmek.
No diyen kullanıcı password-based normal akışı kullanıyor. Yes’in sadece opt-in olanlar için extra friction.
Performans
Face ID ~1 saniye, Touch ID ~0.5 saniye. Bu süre içinde app UI’ının hazır olması gerekiyor. Biometric prompt başarılıysa immediate geçiş, başarısızsa fallback UI.
App başladığında biometric prompt’u splash screen sırasında tetiklerseniz kullanıcı zaten bekliyor, tek beklenti oluyor. Ayrı bir lock screen’den sonra biometric prompt göstermek iki aşamalı bekleme yaratıyor.
Son tavsiye
Biometric auth güvenlik feature’ı ama aynı zamanda UX feature’ı. Doğru kullanıldığında kullanıcı memnun oluyor, yanlış kullanıldığında friction ekliyor.
Prensipler:
– Spesifik reason mesajı
– Error type’a göre kullanıcı mesajı
– Biometric-backed keychain ile hassas veri
– Background lock timing’i dengede
– Opt-in default
– Yüksek risk’te 2FA ile birleştir
Bu 6 prensip uygulanırsa biometric auth app’in güvenlik ve deneyim açısından premium tarafı oluyor.