Android eğitimi kapsamında Credential Manager, WebAuthn challenge doğrulaması ve Digital Asset Links ile parola tabanlı girişten passkey akışına kontrollü geçişi uygulayın.
Android Eğitimi: Credential Manager ile Passkey Geçiş Tasarımı
Android eğitimi için passkey akışının sınırları
Parola ekranına bir passkey butonu eklemek yeterli değildir. Sunucu, her giriş denemesinde kriptografik olarak rastgele, tek kullanımlık ve kısa ömürlü bir WebAuthn challenge üretmelidir. Challenge'ı kullanıcının oturum öncesi kimliği, beklenen rpId ve son kullanma zamanı ile veritabanında saklayın. İstemcinin challenge üretmesine izin verirseniz, saldırgan kendi challenge'ını imzalatıp tekrar oynatma denemesi yapabilir. Android istemcisi sunucudan sadece PublicKeyCredentialRequestOptions benzeri JSON almalı, Credential Manager sonucundaki authenticationResponseJson değerini değiştirmeden geri göndermelidir.
Bu sınırı android mvvm mimarisi içinde net tutun: ViewModel HTTP isteği ile options JSON'unu ister, Compose katmanı yalnızca Activity gerektiren Credential Manager çağrısını yapar, repository ise assertion JSON'unu sunucuya yollar. Activity referansını ViewModel'e enjekte etmeyin. ViewModel process death sonrası tutulabilirken Activity referansı eski pencereye bağlı kalabilir. Bu ayrım, kotlin eğitimi veya android kursu projelerinde sık görülen Context leak problemini doğrudan engeller.
interface PasskeyRepository {
suspend fun requestOptions(loginHint: String?): String
suspend fun verifyAssertion(assertionJson: String): Session
}
class SignInViewModel(
private val repository: PasskeyRepository
) : ViewModel() {
suspend fun beginPasskey(loginHint: String?): String =
repository.requestOptions(loginHint)
suspend fun completePasskey(assertionJson: String): Session =
repository.verifyAssertion(assertionJson)
}Credential Manager bağımlılıklarını uygulama modülüne ekleyin; Google Play services sağlayıcısını kullanacaksanız ikinci artifact gerekir. Sürümü version catalog veya kurumunuzun dependency yönetiminden resolve edin. Bu yapı, platform sağlayıcısı uygun olmadığında Play services tabanlı sağlayıcının credential seçim arayüzüne katılmasını sağlar.
Jetpack Compose ile Credential Manager çağrısını yaşam döngüsünde yürütmek
jetpack compose içinde Credential Manager çağrısını doğrudan Button onClick bloğunda başlatmak yerine, network ile alınan request JSON'unu ViewModel'den alın ve çağrıyı Activity context ile yapın. getCredential kullanıcı arayüzü açabildiği için applicationContext kullanmak doğru değildir. Aşağıdaki örnekte ComponentActivity zorunluluğu açıkça kontrol edilir; preview, test veya yanlış Context zincirinde sessizce başarısız olmak yerine hatayı görünür yapar.
@Composable
fun PasskeySignInScreen(viewModel: SignInViewModel) {
val context = LocalContext.current
val activity = context as? ComponentActivity
?: error("Credential Manager requires an Activity context")
val scope = rememberCoroutineScope()
val credentialManager = remember(activity) {
CredentialManager.create(activity)
}
Button(onClick = {
scope.launch {
try {
val optionsJson = viewModel.beginPasskey(loginHint = null)
val request = GetCredentialRequest(
credentialOptions = listOf(
GetPublicKeyCredentialOption(optionsJson)
)
)
val result = credentialManager.getCredential(activity, request)
val credential = result.credential as? PublicKeyCredential
?: error("Expected a public-key credential")
viewModel.completePasskey(credential.authenticationResponseJson)
} catch (e: GetCredentialException) {
// TYPE_USER_CANCELED ise hata toast'i yerine ekranda kalın.
Log.i("Passkey", "Credential flow ended: ${e.type}")
}
}
}) {
Text("Passkey ile gir")
}
}Kritik ayrıntı: kullanıcı credential seçiciyi kapattığında bunu başarısız kimlik doğrulama saymayın. GetCredentialException.type değerini telemetry olayında canceled, no_credential ve provider_error olarak ayırın. Örneğin no_credential oranı yüksekse önce kayıt akışını göstermelisiniz; canceled oranı yüksekse seçim ekranına girmeden önce kullanıcının neden passkey kullanacağını açıklayan ara UI'ı A/B test etmek anlamlı olabilir. Bu sınıflandırma, mobil uygulama geliştirme eğitimi içeriğinde çoğunlukla atlanan ürün ve hata ayıklama ayrımını sağlar.
Digital Asset Links ve sunucu tarafı WebAuthn doğrulaması
Native Android uygulamasının web sitenizin relying party kimliğiyle passkey kullanabilmesi için https://ornek.com/.well-known/assetlinks.json altında Digital Asset Links belgesi yayınlayın. relation alanında get_login_creds ilişkisini kullanın. package_name ile Play'e yüklenen uygulamanın gerçek applicationId değeri, sha256_cert_fingerprints ile de release imza sertifikasının SHA-256 parmak izi eşleşmelidir. Debug anahtarının parmak izini production dosyasına koymak, yerel testte çalışıp dağıtımda başarısız olan klasik hatadır.
[
{
"relation": [
"delegate_permission/common.get_login_creds"
],
"target": {
"namespace": "android_app",
"package_name": "com.acme.product",
"sha256_cert_fingerprints": [
"AB:CD:EF:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D"
]
}
}
]Sunucuda assertion JSON'u parse edip sadece credentialId eşleşmesini kontrol etmek güvenli değildir. WebAuthn doğrulayıcısı clientDataJSON.type alanının webauthn.get olduğunu, challenge'ın veritabanındaki kullanılmamış challenge ile byte düzeyinde eşleştiğini, origin ve rpIdHash değerlerinin beklenen alan adını temsil ettiğini, authenticatorData içindeki user presence bayrağını ve imzanın kayıtlı public key ile geçerli olduğunu doğrulamalıdır. Ardından signature counter geriye gittiyse bunu risk olayı olarak kaydedin. Bazı senkronize passkey sağlayıcılarında counter artışı garantili değildir; bu nedenle tek başına counter farkı ile kullanıcıyı kilitlemek yerine ek doğrulama veya oturum risk skoru kullanın.
Dağıtım öncesi ilişkiyi cihazda ölçün:
adb shell pm verify-app-links --re-verify com.acme.product
adb shell pm get-app-links com.acme.product
curl -i https://ornek.com/.well-known/assetlinks.json Komut çıktısında doğrulama durumu başarısızsa önce HTTP yönlendirmesi, Content-Type, CDN cache ve release sertifika parmak izini inceleyin. Bu kontrol, play store yayınlama sonrasında fark edilen alan adı ilişkilendirme hatasını CI smoke testine taşır.Android Studio eğitimi kapsamında kayıt ve geri dönüş akışı
Kayıt için sunucu create challenge ve user.id içeren PublicKeyCredentialCreationOptions JSON üretir. İstemci bu JSON'u CreatePublicKeyCredentialRequest ile işletim sistemi sağlayıcısına iletir. Kayıt yanıtını sunucuya gönderdikten sonra credentialId, COSE public key, transports ve kullanıcı kimliğini saklayın. Aynı kullanıcı için birden çok credential kaydı kabul edin; telefon değişimi veya bir sağlayıcının kaldırılması, tek credential tasarımında hesabı kurtarma sürecine dönüştürür.
suspend fun registerPasskey(
activity: ComponentActivity,
creationOptionsJson: String,
api: RegistrationApi
) {
val manager = CredentialManager.create(activity)
val result = manager.createCredential(
activity,
CreatePublicKeyCredentialRequest(creationOptionsJson)
)
val credential = result as? CreatePublicKeyCredentialResponse
?: error("Expected public-key registration response")
api.verifyRegistration(credential.registrationResponseJson)
}Android Studio'da gerçek cihaz matrisi oluşturun: en az bir Google Password Manager etkin cihaz, farklı bir credential sağlayıcısı olan cihaz ve credentialsiz cihaz kullanın. Unit testte CredentialManager'ı doğrudan mocklamak yerine yukarıdaki çağrıyı PasskeyClient arayüzü arkasına alın; fake implementasyon ile registration JSON ve cancellation sonucunu deterministik üretin. Böylece kotlin kursu katılımcılarının sık yaptığı, framework final sınıfını kırılgan mock'lama yaklaşımından kaçınırsınız.
Geri dönüş planında parola veya doğrulanmış e-posta tabanlı account recovery'yi passkey kaydından bağımsız tutun. Recovery tamamlandığında eski session token'larını revoke edin ve yeni passkey kaydını audit log'a yazın. android eğitim materyalinde bu adımın atlanması, cihazını kaybeden kullanıcının hesabına tekrar erişememesi veya saldırganın eski oturumu sürdürmesiyle sonuçlanır.
Geçişi ölçmek: olay şeması, hata bütçesi ve kontrollü yayın
Geçiş başarısını yalnızca giriş sayısıyla ölçmeyin. Analytics veya kendi event hattınızda passkey_offer_shown, passkey_get_started, passkey_get_succeeded, passkey_get_canceled, passkey_get_no_credential ve server_assertion_rejected olaylarını kaydedin. Her olaya app version, provider package, hata tipi ve anonimleştirilmiş cohort ekleyin; assertion JSON, credentialId veya e-posta gibi kimlik doğrulama verilerini loglamayın.
Önce parola akışında oturum açan kullanıcıların küçük bir cohort'una passkey kayıt önerisini açın. Önce-sonra karşılaştırmasında passkey_get_started / passkey_offer_shown oranını başlatma, passkey_get_succeeded / passkey_get_started oranını tamamlanma, server_assertion_rejected / passkey_get_started oranını sunucu reddi olarak hesaplayın. Sunucu reddi artarsa istemci tarafında yeniden deneme eklemek yerine challenge tüketim zamanını, yük dengeleyiciler arasındaki challenge store tutarlılığını ve rpId doğrulamasını inceleyin. Bu yaklaşım android kursu projelerinde görülen 'UI çalışıyor, demek ki login doğru' varsayımını somut telemetri ile çürütür.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Android eğitimi projelerinde Credential Manager neden applicationContext ile çalışmıyor?
getCredential ve createCredential kullanıcıya credential seçici gösterebildiği için Activity tabanlı bir pencereye ihtiyaç duyar. Compose'da LocalContext.current değerini ComponentActivity olarak kontrol edin; applicationContext'i CredentialManager.create için kullanıp sonra UI çağrısı yapmak pencere ve yaşam döngüsü hatalarına yol açabilir.
Jetpack Compose uygulamasında passkey challenge istemcide üretilebilir mi?
Hayır. Challenge sunucuda CSPRNG ile üretilmeli, tek kullanımlık olarak saklanmalı ve kısa bir TTL ile sınırlandırılmalıdır. İstemci yalnızca GetPublicKeyCredentialOption'a sunucunun JSON'unu verir ve authenticationResponseJson sonucunu sunucuya iletir.
Play Store yayınlama sonrası Digital Asset Links neden doğrulanmaz?
En yaygın neden release imzası ile assetlinks.json içindeki SHA-256 parmak izinin farklı olmasıdır. Ayrıca dosya tam olarak /.well-known/assetlinks.json yolunda HTTPS ile erişilebilir olmalıdır. adb shell pm get-app-links paket.adı ve curl -i URL komutlarıyla cihaz durumu ile HTTP cevabını ayrı ayrı kontrol edin.
Android MVVM mimarisi içinde Credential Manager çağrısı ViewModel'de mi olmalı?
Hayır. ViewModel options JSON'unu almak ve assertion'ı doğrulatmak için repository çağırmalıdır. Activity gerektiren Credential Manager çağrısı Compose UI veya Activity katmanında kalmalı; sonuç olarak yalnızca JSON ve domain sonucu ViewModel'e iletilmelidir.
AI / LLM Discovery
Bu makale Opendart Akademi Android eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


