1. Başlamadan önce
Geleneksel kimlik doğrulama çözümleri, güvenlik ve kullanılabilirlik açısından çeşitli zorluklar oluşturur.
Şifreler yaygın olarak kullanılsa da...
- Kolayca unutulur
- Kullanıcıların güçlü şifreler oluşturmak için bilgi sahibi olması gerekir.
- Saldırganlar tarafından kolayca kimlik avı, toplanabilir ve yeniden oynatılabilir.
Android, şifresiz kimlik doğrulama için yeni nesil endüstri standardı olan geçiş anahtarlarını destekleyerek oturum açma deneyimini basitleştirmek ve güvenlik risklerini ele almak amacıyla Kimlik Bilgisi Yöneticisi API'sini oluşturmak için çalıştı.
Kimlik Bilgisi Yöneticisi, geçiş anahtarlarını destekler ve bu desteği şifreler, Google ile oturum açma gibi geleneksel kimlik doğrulama yöntemleriyle birleştirir.
Kullanıcılar geçiş anahtarları oluşturabilir ve bunları Google Şifre Yöneticisi'nde saklayabilir. Google Şifre Yöneticisi, bu geçiş anahtarlarını kullanıcının oturum açtığı Android cihazlar arasında senkronize eder. Kullanıcının geçiş anahtarıyla oturum açabilmesi için geçiş anahtarının oluşturulması, bir kullanıcı hesabıyla ilişkilendirilmesi ve herkese açık anahtarının bir sunucuda depolanması gerekir.
Bu codelab'de, kimlik bilgisi yöneticisi API'sini kullanarak şifre anahtarlarını ve şifreyi nasıl kaydedeceğinizi ve gelecekte kimlik doğrulama amacıyla nasıl kullanacağınızı öğreneceksiniz. Aşağıdakiler dahil olmak üzere 2 akış vardır:
- Kaydolun : Şifre anahtarları ve şifre kullanarak.
- Oturum açma : Geçiş anahtarları ve kayıtlı şifre kullanılarak.
Ön koşullar
- Android Studio'da uygulamaların nasıl çalıştırıldığı hakkında temel düzeyde bilgi sahibi olmanız gerekir.
- Android uygulamalarındaki kimlik doğrulama akışı hakkında temel düzeyde bilgi sahibi olmanız gerekir.
- Geçiş anahtarları hakkında temel düzeyde bilgi sahibi olmanız gerekir.
Neler öğreneceksiniz?
- Geçiş anahtarı oluşturma.
- Şifreleri şifre yöneticisine kaydetme
- Kullanıcıların geçiş anahtarı veya kayıtlı şifreyle kimlik doğrulaması nasıl yapılır?
İhtiyacınız olanlar
Aşağıdaki cihaz kombinasyonlarından biri:
- Android 9 veya sonraki sürümleri (geçiş anahtarları için) ve Android 4.4 veya sonraki sürümleri(Kimlik Bilgisi Yöneticisi API'si aracılığıyla şifre kimlik doğrulaması için) çalıştıran bir Android cihaz.
- Tercihen biyometrik sensörlü cihaz.
- Biyometri (veya ekran kilidi) kaydettiğinizden emin olun.
- Kotlin eklentisi sürümü : 1.8.10
2. Hazırlanın
- Bu deposu dizüstü bilgisayarınızda credman_codelab şubesinden klonlayın : https://github.com/android/identity-samples/tree/credman_codelab
git clone -b credman_codelab https://github.com/android/identity-samples.git
- CredentialManager modülüne gidin ve projeyi Android Studio'da açın.
Uygulamanın ilk durumunu görelim
Uygulamanın ilk durumunun nasıl çalıştığını görmek için aşağıdaki adımları uygulayın:
- Uygulamayı başlatın.
- Kaydol ve oturum aç düğmelerinin bulunduğu bir ana ekran görürsünüz. Bu düğmeler henüz herhangi bir işlev sunmuyor ancak işlevlerini sonraki bölümlerde etkinleştireceğiz.
3. Geçiş anahtarları kullanarak kaydolma özelliğini ekleme
Kullanıcılar, Kimlik Bilgisi Yöneticisi API'sini kullanan bir Android uygulamasında yeni bir hesaba kaydolurken hesapları için geçiş anahtarı oluşturabilir. Bu geçiş anahtarı, kullanıcının seçtiği kimlik bilgisi sağlayıcısında güvenli bir şekilde depolanır ve kullanıcının her seferinde şifresini girmesine gerek kalmadan gelecekteki oturum açma işlemleri için kullanılır.
Ardından, geçiş anahtarı oluşturacak ve biyometrik/ekran kilidi kullanarak kullanıcı kimlik bilgilerini kaydedeceksiniz.
Geçiş anahtarıyla kaydolma
Credential Manager/app/main/java/SignUpFragment.kt içindeki kod, "username" adlı bir metin alanı ve geçiş anahtarıyla kaydolma düğmesi tanımlar.
Zorluk ve diğer JSON yanıtını bir createPasskey() çağrısına iletin
Geçiş anahtarı oluşturulmadan önce, createCredential() çağrısı sırasında Credential Manager API'ye iletilecek gerekli bilgileri sunucudan istemeniz gerekir.
Projenizin öğelerinde, bu kod laboratuvarındaki gerekli parametreleri döndüren RegFromServer.txt adlı bir örnek yanıt zaten mevcuttur.
- Uygulamanızda SignUpFragment.kt dosyasına gidin. Geçiş anahtarı oluşturma ve kullanıcının giriş yapmasına izin verme mantığını yazacağınız signUpWithPasskeys yöntemini bulun. Yöntemi aynı sınıfta bulabilirsiniz.
createPasskey()
işlevini çağıracak bir yorumla else bloğunu kontrol edin ve aşağıdaki kodla değiştirin:
SignUpFragment.kt
//TODO : Call createPasskey() to signup with passkey
val data = createPasskey()
Bu yöntem, ekranda geçerli bir kullanıcı adı doldurduktan sonra çağrılır.
createPasskey()
yönteminin içinde, gerekli parametrelerin döndürüldüğü birCreatePublicKeyCredentialRequest()
oluşturmanız gerekir.
SignUpFragment.kt
//TODO create a CreatePublicKeyCredentialRequest() with necessary registration json from server
val request = CreatePublicKeyCredentialRequest(fetchRegistrationJsonFromServer())
fetchRegistrationJsonFromServer()
yöntemi, öğelerden taklit edilmiş bir sunucu PublicKeyCredentialCreationOptions
JSON yanıtı okur ve geçiş anahtarı oluşturulurken iletilecek kayıt JSON'unu döndürür.
fetchRegistrationJsonFromServer()
yöntemini bulun ve JSON döndürmek için TODO'yu aşağıdaki kodla değiştirin. Ayrıca boş dize döndürme ifadesini kaldırın :
SignUpFragment.kt
//TODO fetch registration mock response
val response = requireContext().readFromAsset("RegFromServer")
//Update userId,challenge, name and Display name in the mock
return response.replace("<userId>", getEncodedUserId())
.replace("<userName>", binding.username.text.toString())
.replace("<userDisplayName>", binding.username.text.toString())
.replace("<challenge>", getEncodedChallenge())
- Bu JSON eksik ve değiştirilmesi gereken 4 alanı var.
- Kullanıcının birden fazla geçiş anahtarı oluşturabilmesi için UserId'nin benzersiz olması gerekir (gerekirse).
<userId>
değerini, oluşturulanuserId
değeriyle değiştirin. <challenge>
değerinin de benzersiz olması gerekir. Böylece rastgele bir benzersiz meydan okuma oluşturursunuz. Yöntem zaten kodunuzdadır.
Gerçek bir sunucu PublicKeyCredentialCreationOptions
yanıtı daha fazla seçenek döndürebilir. Aşağıda bu alanların bazılarını içeren bir örnek verilmiştir:
{
"challenge": String,
"rp": {
"name": String,
"id": String
},
"user": {
"id": String,
"name": String,
"displayName": String
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Aşağıdaki tabloda, PublicKeyCredentialCreationOptions
nesnesinde bulunan önemli parametrelerden bazıları açıklanmaktadır:
Parametreler | Açıklamalar |
Tahmin edilmesini imkansız kılacak kadar entropi içeren, sunucu tarafından oluşturulan rastgele bir dize. En az 16 bayt uzunluğunda olmalıdır. Bu, onaylama yapılmadığı sürece kayıt sırasında gereklidir ancak kullanılmaz. | |
Kullanıcının benzersiz kimliği. Bu değer, e-posta adresleri veya kullanıcı adları gibi kimliği tanımlayabilecek bilgiler içermemelidir. Hesap başına rastgele oluşturulan 16 baytlık bir değer işe yarayacaktır. | |
Bu alan, kullanıcının tanıyacağı hesap için benzersiz bir tanımlayıcı (ör. e-posta adresi veya kullanıcı adı) içermelidir. Bu, hesap seçicide gösterilir. (Kullanıcı adı kullanıyorsanız şifre kimlik doğrulamasında kullanılanla aynı değeri kullanın.) | |
Bu alan, hesap için isteğe bağlı ve daha kullanıcı dostu bir addır. | |
Güvenen Taraf Tüzel Kişiliği, başvuru ayrıntılarınıza karşılık gelir. Aşağıdaki özelliklere sahiptir:
| |
İzin verilen algoritmaların ve anahtar türlerinin listesi. Bu liste en az bir öğe içermelidir. | |
Cihaz kaydettirmeye çalışan kullanıcı başka cihazlar da kaydettirmiş olabilir. Tek bir kimlik doğrulayıcıda aynı hesap için birden fazla kimlik bilgisi oluşturulmasını sınırlamak üzere bu cihazları yoksayabilirsiniz. Sağlandıysa | |
Cihazın platforma eklenip eklenmeyeceğini veya eklenmesinin zorunlu olup olmadığını belirtir. Bu değeri | |
| Geçiş anahtarı oluşturmak için |
Kimlik bilgisi oluşturma
- Bir
CreatePublicKeyCredentialRequest()
oluşturduktan sonra, oluşturulan isteği kullanarakcreateCredential()
çağrısını çağırmanız gerekir.
SignUpFragment.kt
//TODO call createCredential() with createPublicKeyCredentialRequest
try {
response = credentialManager.createCredential(
requireActivity(),
request
) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
configureProgress(View.INVISIBLE)
handlePasskeyFailure(e)
}
- Gerekli bilgileri
createCredential()
'e iletirsiniz. - İstek başarılı olduğunda ekranınızda geçiş anahtarı oluşturmanızı isteyen bir alt sayfa görürsünüz.
- Artık kullanıcılar kimliklerini biyometri veya ekran kilidi gibi yöntemlerle doğrulayabilir.
- Oluşturulan görünümlerin görünürlüğünü ve istek herhangi bir nedenle başarısız olursa istisnaları siz yönetirsiniz. Burada hata mesajları günlüğe kaydedilir ve uygulamada bir hata iletişim kutusunda gösterilir. Hata günlüklerinin tamamını Android Studio veya
adb debug
komutunu kullanarak kontrol edebilirsiniz.
- Son olarak, kayıt işlemini tamamlamanız gerekir. Uygulama, sunucuya bir ortak anahtar kimlik bilgisi gönderir ve bu kimlik bilgisi mevcut kullanıcıya kaydedilir.
Burada bir örnek sunucu kullandık. Bu nedenle, sunucunun gelecekteki kimlik doğrulama ve doğrulama işlemleri için kayıtlı ortak anahtarı kaydettiğini belirten true değerini döndürüyoruz. Kendi uygulamanız için sunucu tarafı geçiş anahtarı kaydı hakkında daha fazla bilgi edinebilirsiniz.
signUpWithPasskeys()
yönteminde ilgili yorumu bulun ve aşağıdaki kodla değiştirin:
SignUpFragment.kt
//TODO : complete the registration process after sending public key credential to your server and let the user in
data?.let {
registerResponse()
DataProvider.setSignedInThroughPasskeys(true)
listener.showHome()
}
registerResponse()
, sahte sunucunun ortak anahtarı gelecekte kullanılmak üzere kaydettiğini belirtentrue
değerini döndürür.setSignedInThroughPasskeys
işaretinitrue
olarak ayarlayın.- Kullanıcı giriş yaptıktan sonra ana ekrana yönlendirilir.
Gerçek bir PublicKeyCredential
daha fazla alan içerebilir. Bu alanlara örnek olarak aşağıdaki gösterilmiştir:
{
"id": String,
"rawId": String,
"type": "public-key",
"response": {
"clientDataJSON": String,
"attestationObject": String,
}
}
Aşağıdaki tabloda, PublicKeyCredential
nesnesinde bulunan önemli parametrelerden bazıları açıklanmaktadır:
Parametreler | Açıklamalar |
Oluşturulan geçiş anahtarının Base64URL kodlamalı kimliği. Bu kimlik, tarayıcıya kimlik doğrulamasından sonra cihazda eşleşen bir geçiş anahtarı olup olmadığını belirlemesine yardımcı olur. Bu değer, arka uçtaki veritabanında depolanmalıdır. | |
Kimlik bilgisi kimliğinin | |
İstemci verileri kodlanmış bir | |
|
Uygulamayı çalıştırdığınızda Geçiş anahtarlarıyla kaydolun düğmesini tıklayıp geçiş anahtarı oluşturabilirsiniz.
4. Kimlik Bilgisi Sağlayıcı'ya şifre kaydetme
Bu uygulamada, Kayıt ekranınızda, gösterim amacıyla uygulanmış bir kullanıcı adı ve şifreyle kayıt özelliği zaten mevcuttur.
Kullanıcı şifresi kimlik bilgisini şifre sağlayıcısına kaydetmek için şifreyi kaydetmek üzere createCredential()
'a iletilecek bir CreatePasswordRequest
uygularsınız.
signUpWithPassword()
yöntemini bulun, TODO'yu bircreatePassword
çağrısıyla değiştirin:
SignUpFragment.kt
//TODO : Save the user credential password with their password provider
createPassword()
createPassword()
yönteminin içinde, aşağıdaki gibi bir şifre isteği oluşturmanız gerekir. TODO'yu aşağıdaki kodla değiştirin:
SignUpFragment.kt
//TODO : CreatePasswordRequest with entered username and password
val request = CreatePasswordRequest(
binding.username.text.toString(),
binding.password.text.toString()
)
- Ardından,
createPassword()
yönteminin içinde bir şifre oluşturma isteği içeren bir kimlik bilgisi oluşturun ve kullanıcı şifresi kimlik bilgisini şifre sağlayıcısına kaydedin. TODO'yu aşağıdaki kodla değiştirin:
SignUpFragment.kt
//TODO : Create credential with created password request
try {
credentialManager.createCredential(requireActivity(), request) as CreatePasswordResponse
} catch (e: Exception) {
Log.e("Auth", " Exception Message : " + e.message)
}
- Artık şifreyle kimliğinizi doğrulamak için tek bir dokunuşla şifre kimlik bilgisini kullanıcının şifre sağlayıcısına başarıyla kaydettiniz.
5. Geçiş anahtarı veya şifreyle kimlik doğrulama özelliğini ekleme
Artık uygulamanızda güvenli bir şekilde kimlik doğrulama yapmak için bu anahtarı kullanmaya hazırsınız.
getPasskey() çağrısına iletilecek meydan okumayı ve diğer seçenekleri alın
Kullanıcıdan kimlik doğrulaması yapmasını istemeden önce, sunucudan WebAuthn JSON'a iletilecek parametreleri (bir istem de dahil) istemeniz gerekir.
Öğelerinizde, bu kod laboratuvarındaki bu tür parametreleri döndüren bir örnek yanıt (AuthFromServer.txt) zaten mevcuttur.
- Uygulamanızda SignInFragment.kt dosyasına gidin, kayıtlı geçiş anahtarı veya şifreyle kimlik doğrulama mantığını yazacağınız ve kullanıcının giriş yapmasına izin vereceğiniz
signInWithSavedCredentials
yöntemini bulun: createPasskey()
işlevini çağırmak için else bloğunu bir yorumla kontrol edin ve aşağıdaki kodla değiştirin:
SignInFragment.kt
//TODO : Call getSavedCredentials() method to signin using passkey/password
val data = getSavedCredentials()
- getSavedCredentials() yönteminin içinde, kimlik bilgisi sağlayıcınızdan kimlik bilgisi almak için gerekli parametreleri içeren bir
GetPublicKeyCredentialOption()
oluşturmanız gerekir.
SigninFragment.kt
//TODO create a GetPublicKeyCredentialOption() with necessary registration json from server
val getPublicKeyCredentialOption =
GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null)
fetchAuthJsonFromServer()
yöntemi, öğelerden kimlik doğrulama JSON yanıtını okur ve bu kullanıcı hesabıyla ilişkili tüm geçiş anahtarlarını almak için kimlik doğrulama JSON'unu döndürür.
GetPublicKeyCredentialOption() işlevinin 2. parametresi clientDataHash
'tür. Bu, güvenen tarafın kimliğini doğrulamak için kullanılan bir karma oluşturma işlemidir. Bunu yalnızca GetCredentialRequest.origin
öğesini ayarlarsanız ayarlayın. Örnek uygulamada bu değer null
olarak ayarlanmıştır.
- fetchAuthJsonFromServer() yöntemini bulun ve TODO'yu JSON döndürmek için aşağıdaki kodla değiştirin. Ayrıca boş dize döndürme ifadesini kaldırın:
SignInFragment.kt
//TODO fetch authentication mock json
return requireContext().readFromAsset("AuthFromServer")
Not : Bu kod laboratuvarının sunucusu, API'nin getCredential() çağrısına iletilen PublicKeyCredentialRequestOptions
sözlüğüne mümkün olduğunca benzer bir JSON döndürecek şekilde tasarlanmıştır. Aşağıdaki kod snippet'inde, gerçek bir yanıtta alabileceğiniz birkaç örnek seçenek verilmiştir:
{
"challenge": String,
"rpId": String,
"userVerification": "",
"timeout": 1800000
}
Aşağıdaki tabloda, PublicKeyCredentialRequestOptions
nesnesinde bulunan önemli parametrelerden bazıları açıklanmaktadır:
Parametreler | Açıklamalar |
| |
RP kimliği bir alandır. Web siteleri, alanlarını veya kaydedilebilen bir son ek belirtebilir. Bu değer, geçiş anahtarı oluşturulurken kullanılan |
- Ardından, bu kullanıcı hesabının Kimlik Bilgisi Yöneticisi API'si aracılığıyla şifre sağlayıcınıza kaydedilen tüm şifreleri almak için bir
PasswordOption()
nesnesi oluşturmanız gerekir.getSavedCredentials()
yönteminde TODO'yu bulup aşağıdakiyle değiştirin:
SigninFragment.kt
//TODO create a PasswordOption to retrieve all the associated user's password
val getPasswordOption = GetPasswordOption()
Kimlik bilgisi alma
- Ardından, ilişkili kimlik bilgilerini almak için
getCredential()
isteğini yukarıdaki tüm seçeneklerle çağırmanız gerekir:
SignInFragment.kt
//TODO call getCredential() with required credential options
val result = try {
credentialManager.getCredential(
requireActivity(),
GetCredentialRequest(
listOf(
getPublicKeyCredentialOption,
getPasswordOption
)
)
)
} catch (e: Exception) {
configureViews(View.INVISIBLE, true)
Log.e("Auth", "getCredential failed with exception: " + e.message.toString())
activity?.showErrorAlert(
"An error occurred while authenticating through saved credentials. Check logs for additional details"
)
return null
}
if (result.credential is PublicKeyCredential) {
val cred = result.credential as PublicKeyCredential
DataProvider.setSignedInThroughPasskeys(true)
return "Passkey: ${cred.authenticationResponseJson}"
}
if (result.credential is PasswordCredential) {
val cred = result.credential as PasswordCredential
DataProvider.setSignedInThroughPasskeys(false)
return "Got Password - User:${cred.id} Password: ${cred.password}"
}
if (result.credential is CustomCredential) {
//If you are also using any external sign-in libraries, parse them here with the utility functions provided.
}
- Gerekli bilgileri
getCredential()
'e iletirsiniz. Bu, kimlik bilgisi seçeneklerinin listesini ve alttaki sayfada seçenekleri bu bağlamda oluşturmak için bir etkinlik bağlamı alır. - İstek başarılı olduğunda, ekranınızda ilişkili hesap için oluşturulan tüm kimlik bilgilerinin listelendiği bir alt sayfa görürsünüz.
- Artık kullanıcılar, seçilen kimlik bilgisinin kimliğini doğrulamak için biyometrik veriler veya ekran kilidi gibi yöntemlerle kimliklerini doğrulayabilir.
- Seçilen kimlik bilgisi bir
PublicKeyCredential
isesetSignedInThroughPasskeys
işaretinitrue
olarak ayarlayın. Aksi takdirdefalse
değerine ayarlayın.
Aşağıdaki kod snippet'inde örnek bir PublicKeyCredential
nesnesi bulunmaktadır:
{
"id": String
"rawId": String
"type": "public-key",
"response": {
"clientDataJSON": String
"authenticatorData": String
"signature": String
"userHandle": String
}
}
Aşağıdaki tabloda tüm parametreler yer almamakla birlikte PublicKeyCredential
nesnesinde önemli olan parametreler bulunmaktadır:
Parametreler | Açıklamalar |
Kimliği doğrulanmış geçiş anahtarı kimliğinin Base64URL kodlu kimliği. | |
Kimlik bilgisi kimliğinin | |
İstemci verilerinden oluşan bir | |
Kimlik doğrulayıcı verilerinin | |
İmzanın | |
Oluşturma sırasında ayarlanan kullanıcı kimliğini içeren bir |
- Son olarak kimlik doğrulama işlemini tamamlamanız gerekir. Normalde, kullanıcı geçiş anahtarı kimlik doğrulamasını tamamladıktan sonra uygulama, kimlik doğrulama beyanı içeren bir ortak anahtar kimlik bilgisi gönderir. Bu kimlik bilgisi, beyanı doğrular ve kullanıcının kimliğini doğrular.
Burada bir örnek sunucu kullandık. Bu nedenle, sunucunun iddiayı doğruladığını belirten true
değerini döndürüyoruz. Kendi uygulamanız için sunucu tarafı geçiş anahtarı kimlik doğrulaması hakkında daha fazla bilgi edinebilirsiniz.
signInWithSavedCredentials()
yönteminde ilgili yorumu bulun ve aşağıdaki kodla değiştirin:
SignInFragment.kt
//TODO : complete the authentication process after validating the public key credential to your server and let the user in.
data?.let {
sendSignInResponseToServer()
listener.showHome()
}
sendSigninResponseToServer()
, (sahte) sunucunun gelecekte kullanılmak üzere ortak anahtarı doğruladığını belirten true değerini döndürür.- Kullanıcı giriş yaptıktan sonra ana ekrana yönlendirilir.
Uygulamayı çalıştırın ve oturum açma > Şifre anahtarlarıyla/kayıtlı şifreyle oturum açma bölümüne gidin ve kayıtlı kimlik bilgilerini kullanarak oturum açmayı deneyin.
Dene
Android uygulamanızda geçiş anahtarı oluşturma, şifreyi Kimlik Bilgisi Yöneticisi'ne kaydetme ve Kimlik Bilgisi Yöneticisi API'sini kullanarak geçiş anahtarları veya kayıtlı şifre üzerinden kimlik doğrulama özelliklerini uyguladınız.
6. Tebrikler!
Bu codelab'i tamamladınız. Son çözümü kontrol etmek istiyorsanız https://github.com/android/identity-samples/tree/main/CredentialManager adresini ziyaret edin.
Sorularınız varsa passkey
etiketiyle Stack Overflow'da sorabilirsiniz.