Kimlik bilgilerinizi cüzdan uygulamanızla sunma

Credential Manager Holder API, Android cüzdan uygulamanızın dijital kimlik bilgilerini yönetmesini ve doğrulayıcılara sunmasını sağlar. Bu kılavuzda, cüzdan uygulamanızın kimlik bilgilerini Kimlik Bilgisi Yöneticisi'ne nasıl kaydedeceğiniz ve kullanıcı bir kimlik bilgisi seçtiğinde sunu isteğini nasıl işleyeceğiniz açıklanmaktadır. Holder API, Android 6 (API düzeyi 23) ve sonraki sürümlerde desteklenir.

Bağımlılık ekleme

Kimlik Bilgisi Yöneticisi Holder API'yi kullanmak için uygulama modülünüzün build.gradle komut dosyasına aşağıdaki bağımlılıkları ekleyin:

dependencies {
    // Used to implement credential registries
    implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-digitalcredentials-openid:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-digitalcredentials-sdjwtvc:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha05"
}

Kimlik bilgilerinizi kaydetme

Öncelikle, OpenId4VpRegistry isteğini kaydetmek için kullanacağınız bir RegistryManager örneği oluşturun.

// Create the registry manager
val registryManager = RegistryManager.create(context)

OpenId4VpRegistry isteği oluşturma

Doğrulayıcılardan gelen OpenID4VP isteklerini işlemek için bir OpenId4VpRegistry kaydetmeniz gerekir. Cüzdan kimlik bilgilerinizle (ör. sdJwtsFromStorage ve mdocsFromStorage) yüklenmiş yerel veri türleriniz varsa bunları Jetpack DigitalCredentialEntry eşdeğerlerine (SdJwtEntry ve MdocEntry) dönüştürmeniz gerekir.

SD-JWT'leri kayıt defterine ekleme

Her yerel SD-JWT kimlik bilgisini kayıt defteri için bir SdJwtEntry ile eşleyin:

fun mapToSdJwtEntries(sdJwtsFromStorage: List<StoredSdJwtEntry>): List<SdJwtEntry> {
    val list = mutableListOf<SdJwtEntry>()

    for (sdJwt in sdJwtsFromStorage) {
        list.add(
            SdJwtEntry(
                verifiableCredentialType = sdJwt.getVCT(),
                claims = sdJwt.getClaimsList(),
                entryDisplayPropertySet = sdJwt.toDisplayProperties(),
                id = sdJwt.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

Kayıt Defteri'ne mdoc ekleme

Yerel mdoc kimlik bilgilerinizi Jetpack türü MdocEntry ile eşleyin:

fun mapToMdocEntries(mdocsFromStorage: List<StoredMdocEntry>): List<MdocEntry> {
    val list = mutableListOf<MdocEntry>()

    for (mdoc in mdocsFromStorage) {
        list.add(
            MdocEntry(
                docType = mdoc.retrieveDocType(),
                fields = mdoc.getFields(),
                entryDisplayPropertySet = mdoc.toDisplayProperties(),
                id = mdoc.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

Kodla ilgili önemli noktalar

  • Şifrelenmiş kimlikler: ID alanını yapılandırmanın bir yöntemi, şifrelenmiş bir kimlik bilgisi tanımlayıcısı kaydetmektir. Böylece, değeri yalnızca siz çözebilirsiniz.
  • Yerelleştirme: Her iki biçim için de kullanıcı arayüzü görüntüleme alanları yerelleştirilmelidir.

İsteğinizi birleştirme ve kaydetme

Dönüştürülen girişlerinizi birleştirin ve isteği RegistryManager ile kaydedin:

val credentialEntries =
    mapToSdJwtEntries(sdJwtsFromStorage) + mapToMdocEntries(mdocsFromStorage)

val openidRegistryRequest = OpenId4VpRegistry(
    credentialEntries = credentialEntries,
    id = "my-wallet-openid-registry-v1" // A stable, unique ID to identify your registry record.
)

İsteğe bağlı: Satır içi kart verme

Kullanıcı için istenen türde mevcut kimlik bilgisi yoksa cüzdanınıza kimlik bilgisi ekleme seçeneği gösterebilirsiniz. Bunu yapmak için OpenId4VpRegistry öğenize, desteklenen kimlik bilgilerini içeren bir inlineIssuanceEntries listesi ekleyin:

val openidRegistryRequest = OpenId4VpRegistry(
    credentialEntries = credentialEntries,
    inlineIssuanceEntries = emptyList(),
                listOf(
                MdocInlineIssuanceEntry(
                    id = "Issuance",
                    display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
                        subtitle = "Mobile Drivers License, State Id, and Others",
                    ),
                    supportedMdocs = setOf(
                        MdocInlineIssuanceEntry.SupportedMdoc(
                        "eu.europa.ec.eudi.pid.1"
                        ),
                        MdocInlineIssuanceEntry.SupportedMdoc(
                        "org.iso.18013.5.1.mDL1"
                        ),
                    )
                ),
                SdJwtInlineIssuanceEntry(
                    id = "sd-jwt-issuance",
                    display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
                        subtitle = "Mobile Drivers License, State Id, and Others",
                    ),
                    supportedSdJwts = setOf(
                        SdJwtInlineIssuanceEntry.SupportedSdJwt("urn:openid:interop:id:1")
                    )
                )
            ),
    id = "my-wallet-openid-registry-v1"
)

Kimlik bilgilerinizi CredentialManager'a kaydetme

try {
    val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
    // Handle failure
}

Uygulama meta verilerini yönetme

Cüzdan uygulamanızın CredentialManager ile kaydettiği meta veriler aşağıdaki özelliklere sahiptir:

  • Kalıcılık: Bilgiler yerel olarak kaydedilir ve yeniden başlatma işlemlerinden sonra da korunur.
  • Yalıtılmış depolama alanı: Her uygulamanın kayıt defteri kayıtları ayrı ayrı depolanır. Bu, bir uygulamanın başka bir uygulamanın kayıt defteri kayıtlarını değiştiremeyeceği anlamına gelir.
  • Anahtarlı Güncellemeler: Her uygulamanın kayıt defteri kayıtları, kayıtların yeniden tanımlanmasına, güncellenmesine veya silinmesine olanak tanıyan bir id ile anahtarlanır.
  • Meta verileri güncelleme: Uygulamanız her değiştiğinde veya ilk yüklendiğinde kalıcı meta verileri güncellemek iyi bir uygulamadır. Bir kayıt defteri aynı id altında birden çok kez çağrılırsa en son çağrı, önceki tüm kayıtların üzerine yazılır. Güncellemek için eski kaydı temizlemenize gerek kalmadan yeniden kaydolun.

İsteğe bağlı: Eşleştirici oluşturma

Eşleştirici, kayıtlı kimlik bilgilerinizi gelen bir doğrulayıcı isteğine göre filtrelemek için Kimlik Bilgisi Yöneticisi'nin korumalı alanda çalıştırdığı bir Wasm ikili programıdır.

  • Varsayılan eşleştirici: OpenId4VpRegistry sınıfı, örneklendirdiğinizde varsayılan OpenId4VP eşleştiriciyi (OpenId4VpDefaults.DEFAULT_MATCHER) otomatik olarak içerir. Tüm standart OpenID4VP kullanım alanlarında kitaplık, eşleştirmeyi sizin için yapar.
  • Özel eşleştirici: Özel eşleştiriciyi yalnızca kendi eşleştirme mantığını gerektiren standart dışı bir protokolü destekliyorsanız kullanırsınız.

Seçili kimlik bilgilerini işleme

Kullanıcı bir kimlik bilgisi seçtiğinde cüzdan uygulamanızın isteği işlemesi gerekir. androidx.credentials.registry.provider.action.GET_CREDENTIAL intent filtresini dinleyen bir Activity tanımlamanız gerekir. Örnek cüzdanımız bu prosedürü göstermektedir.

Intent, etkinliğinizi Verifier isteği ve çağrı kaynağıyla başlatır. Bu bilgileri PendingIntentHandler.retrieveProviderGetCredentialRequest işleviyle ayıklarsınız. Bu işlem, doğrulayıcı isteğiyle ilişkili tüm bilgileri içeren bir ProviderGetCredentialRequest döndürür. Üç temel bileşen vardır:

Sunu isteğini aşağıdaki kodla işleyebilirsiniz:

request.credentialOptions.forEach { option ->
    if (option is GetDigitalCredentialOption) {
        Log.i(TAG, "Got DC request: ${option.requestJson}")
        processRequest(option.requestJson)
    }
}

Bunun bir örneğini örnek cüzdanda görebilirsiniz.

Doğrulayıcı kimliğini kontrol etme

Öncelikle intent'ten ProviderGetCredentialRequest değerini çıkarın:

val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)

Ayrıcalıklı uygulamaları kontrol edin: Ayrıcalıklı uygulamalar (ör. web tarayıcıları), kaynak parametresini ayarlayarak diğer doğrulayıcılar adına çağrı yapabilir. Bu kaynağı almak için CallingAppInfo getOrigin API'ye ayrıcalıklı ve güvenilir arayanların listesini (JSON biçiminde bir izin verilenler listesi) iletin.

val origin = request?.callingAppInfo?.getOrigin(
    privilegedAppsJson // Your allow list JSON
)

origin boş değilse: packageName ve sertifika parmak izleri, izin verilenler listenizdeki bir uygulamayla eşleşirse kaynak döndürülür. Bu çağrıyı ayrıcalıklı bir çağrı olarak kabul edin ve çağrı uygulamasının imzasını kullanarak kaynağı hesaplamak yerine OpenID4VP yanıtında bu kaynağı ayarlayın.

origin boşsa: Doğrulayıcı isteği bir Android uygulamasından geliyordur. OpenID4VP yanıtıyla eşlenen kaynak, android:apk-key-hash:<encoded SHA 256 fingerprint> olarak hesaplanmalıdır.

val appSigningInfo =
    request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory?.get(0)?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash =
    Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
val computedOrigin = "android:apk-key-hash:$certHash"

Kart Sahibi Kullanıcı Arayüzü'nü oluşturma

Bir kimlik bilgisi seçildiğinde, cüzdan uygulaması çağrılır ve kullanıcıyı uygulamanın kullanıcı arayüzü üzerinden yönlendirir. Bu iş akışını yönetmenin iki standart yolu vardır:

  • Kimlik bilgisini yayınlamak için ek kullanıcı kimlik doğrulaması gerekiyorsa BiometricPrompt API'yi kullanın. Bu durum, örnekte gösterilmektedir.
  • Aksi takdirde, birçok cüzdan, verileri hemen arayan uygulamaya geri ileten boş bir etkinlik oluşturarak sessiz bir dönüşü tercih eder. Bu, kullanıcı tıklamalarını en aza indirir ve daha sorunsuz bir deneyim sağlar.

Kimlik bilgisi yanıtını döndürme

Cüzdan uygulamanız sonucu geri göndermeye hazır olduğunda etkinliği kimlik bilgisi yanıtıyla tamamlayın:

PendingIntentHandler.setGetCredentialResponse(
    resultData,
    GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()

Bir istisna varsa kimlik bilgisi istisnasını da benzer şekilde gönderebilirsiniz:

PendingIntentHandler.setGetCredentialException(
    resultData,
    GetCredentialUnknownException() // Configure the proper exception
)
setResult(RESULT_OK, resultData)
finish()

Kimlik bilgisi yanıtını bağlam içinde döndürmenin tam bir örneği için örnek uygulamaya bakın.