E-posta doğrulamasını uygulama

Bu kılavuzda, Doğrulanabilir Sunumlar için OpenID (OpenID4VP) isteği aracılığıyla Digital Credentials Verifier API kullanılarak doğrulanmış e-posta alma işleminin nasıl uygulanacağı açıklanmaktadır.

Bağımlılık ekleme

Uygulamanızın build.gradle dosyasına, Kimlik Bilgisi Yöneticisi için aşağıdaki bağımlılıkları ekleyin:

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha02")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}

Kimlik Bilgisi Yöneticisi'ni başlatma

CredentialManager nesnesi oluşturmak için uygulamanızı veya etkinlik bağlamınızı kullanın.

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

Dijital kimlik bilgisi isteğini oluşturma

Doğrulama e-postası istemek için GetDigitalCredentialOption içeren bir GetCredentialRequest oluşturun. Bu seçenek, Doğrulanabilir Sunumlar için OpenID (OpenID4VP) isteği olarak biçimlendirilmiş bir requestJson dizesi gerektirir.

OpenID4VP isteği JSON'u belirli bir yapıda olmalıdır. Mevcut sağlayıcılar, dış "digital": {"requests": [...] sarmalayıcısı olan bir JSON yapısını destekler.

    val nonce = generateSecureRandomNonce()

    // This request follows the OpenID4VP spec
    val openId4vpRequest = """
{
  "requests": [
    {
      "protocol": "openid4vp-v1-unsigned",
      "data": {
        "response_type": "vp_token",
        "response_mode": "dc_api",
        "nonce": "$nonce",
        "dcql_query": {
          "credentials": [
            {
              "id": "user_info_query",
              "format": "dc+sd-jwt",
               "meta": {
                  "vct_values": ["UserInfoCredential"]
               },
              "claims": [
                {"path": ["email"]},
                {"path": ["name"]},  
                {"path": ["given_name"]},
                {"path": ["family_name"]},
                {"path": ["picture"]},
                {"path": ["hd"]},
                {"path": ["email_verified"]}
              ]
            }
          ]
        }
      }
    }
  ]
}
"""

    val getDigitalCredentialOption = GetDigitalCredentialOption(requestJson = openId4vpRequest)
    val request = GetCredentialRequest(listOf(getDigitalCredentialOption))

İstek aşağıdaki önemli bilgileri içerir:

  • DCQL sorgusu: dcql_query, kimlik bilgisi türünü ve istenen talepleri (email_verified) belirtir. Doğrulama düzeyini belirlemek için başka taleplerde bulunabilirsiniz. Olası iddialardan birkaçı şunlardır:
    • email_verified: Yanıtta, e-postanın doğrulanıp doğrulanmadığını gösteren bir Boole değeridir.
    • hd (barındırılan alan adı): Yanıtta bu alan boştur.
  • nonce: Her istek için benzersiz, kriptografik olarak güvenli rastgele bir değer oluşturulur. Bu, tekrar saldırılarını önlediği için güvenlik açısından kritik bir öneme sahiptir.
  • UserInfoCredential: Bu değer, kullanıcı özelliklerini içeren belirli bir dijital kimlik bilgisi türünü ifade eder. E-posta doğrulama kullanım alanını ayırt etmek için bu parametrenin isteğe eklenmesi çok önemlidir.

Ardından, openId4vpRequest JSON'u bir GetDigitalCredentialOption içine sarmalayın, bir GetCredentialRequest oluşturun ve getCredential() yöntemini çağırın.

İsteği kullanıcıya sunma

Kimlik Bilgisi Yöneticisi'nin yerleşik kullanıcı arayüzünü kullanarak isteği kullanıcıya gösterin.

try {
    // Requesting Digital Credential from user...
    val result = credentialManager.getCredential(activity, request)

    when (val credential = result.credential) {
        is DigitalCredential -> {
            val responseJsonString = credential.credentialJson

            // Successfully received digital credential response.

            // Next, parse this response and send it to your server.
            // ...
        }

        else -> {
            // handle Unexpected State() - Up to the developer
        }
    }
} catch (e: Exception) {
    // handle exceptions - Up to the developer
}

İstemcide yanıtı ayrıştırma

Yanıtı aldıktan sonra istemcide ön ayrıştırma yapabilirsiniz. Bu, kullanıcı arayüzünü hemen güncellemek (ör. kullanıcının adını göstermek) için kullanışlıdır.

Aşağıdaki kod, ham Seçici Açıklama JWT'sini (SD-JWT) ayıklar ve taleplerinin kodunu çözmek için bir yardımcı kullanır.

// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val vpToken = responseData.getJSONObject("vp_token")

// 2. Extract the raw SD-JWT string
val credentialId = vpToken.keys().next()
val rawSdJwt = vpToken.getJSONArray(credentialId).getString(0)

// 3. Use your parser to get the verified claims
// Server-side validation/parsing is highly recommended.

// Assumes a local parser like the one in our SdJwtParser.kt sample
val claims = SdJwtParser.parse(rawSdJwt)
Log.d("TAG", "Parsed Claims: ${claims.toString(2)}")

// 4. Create your VerifiedUserInfo object with REAL data
val userInfo = VerifiedUserInfo(
    email = claims.getString("email"),
    displayName = claims.optString("name", claims.getString("email"))
)

Yanıtı işleme

Credential Manager API, DigitalCredential yanıtı döndürür.

Aşağıda, ham responseJsonString'nin nasıl göründüğüne ve dahili SD-JWT'yi ayrıştırdıktan sonra hak taleplerinin nasıl göründüğüne dair bir örnek verilmiştir. Bu örnekte, doğrulanmış e-postanın yanı sıra ek meta veriler de gösterilmektedir:

/*
// Example of the raw JSON response from credential.credentialJson:
{
  "vp_token": {
    // This key matches the 'id' you set in your dcql_query
    "user_info_query": [
      // The SD-JWT string (Issuer JWT ~ Disclosures ~ Key Binding JWT)
      "eyJhbGciOiJ...~WyI...IiwgImVtYWlsIiwgInVzZXJAZXhhbXBsZS5jb20iXQ~...~eyJhbGciOiJ..."
    ]
  }
}

// Example of the parsed and verified claims from the SD-JWT on your server:
{
  "cnf": {
    "jwk": {..}
  },
  "exp": 1775688222,
  "iat": 1775083422,
  "iss": "https://verifiablecredentials-pa.googleapis.com",
  "vct": "UserInfoCredential",
  "email": "jane.doe.246745@gmail.com",
  "email_verified": true,
  "given_name": "Jane",
  "family_name": "Doe",
  "name": "Jane Doe",
  "picture": "http://example.com/janedoe/me.jpg",
  "hd": ""
}
 */

Hesap oluşturma için sunucu tarafı doğrulama

Alınan e-posta, şifreleme yöntemiyle doğrulandığı için e-posta OTP doğrulama adımını atlayabilirsiniz. Bu sayede, kaydolma sürecindeki zorlukları önemli ölçüde azaltabilir ve dönüşümü artırabilirsiniz. Bu işlem en iyi şekilde sunucunuzda gerçekleştirilir. İstemci, ham yanıtı (vp_token içeren) ve orijinal tek seferlik rastgele sayıyı yeni bir sunucu uç noktasına gönderir.

Doğrulama için uygulamanız, hesap oluşturmadan veya kullanıcıyı giriş yaptırmadan önce kriptografik doğrulama için tam responseJsonString değerini sunucunuza göndermelidir.

Dijital kimlik bilgisi, sunucunuz için iki önemli doğrulama düzeyi sağlar:

  • Verilerin özgünlüğü: Veren kuruluşun (iss) URL'sinin ve SD-JWT imzasının doğrulanması, bu verilerin güvenilir bir yetkili tarafından verildiğini kanıtlar.
  • Sunucunun kimliği: cnf alanının ve anahtar bağlama (kb) imzasının doğrulanması, kimlik bilgisinin ilk verildiği cihaz tarafından paylaşıldığını onaylayarak başka bir cihazda ele geçirilmesini veya kullanılmasını önler.

Sunucudaki doğrulama işlemi şunları gerçekleştirmelidir:

  • Vereni doğrulama: iss (veren) alanının https://verifiablecredentials-pa.googleapis.com ile eşleştiğinden emin olun.
  • İmzayı doğrulama: https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks adresinde bulunan ortak anahtarları (JWK'ler) kullanarak SD-JWT'nin imzasını kontrol edin.

Tam güvenlik için, tekrar oynatma saldırılarını önlemek amacıyla nonce değerini de doğruladığınızdan emin olun.

Sunucunuz bu adımları birleştirerek hem verilerin gerçekliğini hem de sunucunun kimliğini doğrulayabilir. Böylece, yeni hesap sağlanmadan önce kimlik bilgilerinin ele geçirilmediğinden veya taklit edilmediğinden emin olabilirsiniz.

try {
    // Send the raw credential response and the original nonce to your server.
    // Your server must validate the response. createAccountWithVerifiedCredentials
    // is a custom implementation per each RP for server side verification and account creation.
    val serverResponse = createAccountWithVerifiedCredentials(responseJsonString, nonce)

    // Server returns the new account info (e.g., email, name)
    val claims = JSONObject(serverResponse.json)

    val userInfo = VerifiedUserInfo(
        email = claims.getString("email"),
        displayName = claims.optString("name", claims.getString("email"))
    )

    // handle response - Up to the developer
} catch (e: Exception) {
    // handle exceptions - Up to the developer
}

Geçiş anahtarı oluşturma

Hesabın temel hazırlığı yapıldıktan sonra isteğe bağlı ancak kesinlikle önerilen bir sonraki adım, ilgili hesap için hemen geçiş anahtarı oluşturmaktır. Bu sayede kullanıcılar, güvenli ve şifresiz bir yöntemle oturum açabilir. Bu akış, standart geçiş anahtarı kaydıyla aynıdır.

WebView desteği

Akışın WebView üzerinde çalışması için geliştiricilerin, aktarımı kolaylaştırmak amacıyla bir JavaScript köprüsü (JS Köprüsü) uygulaması gerekir. Bu köprü, WebView nesnesinin yerel uygulamaya sinyal göndermesine olanak tanır. Yerel uygulama, Credential Manager API'ye gerçek çağrıyı yapabilir.

Aşağıdaki kaynakları da incelemenizi öneririz: