Meminta kredensial digital di WebView

Panduan ini menjelaskan cara meminta kredensial digital menggunakan Credential Manager di aplikasi Android yang menggunakan WebView. Gunakan JavaScript Bridge untuk memfasilitasi komunikasi antara halaman web dan aplikasi.

Ringkasan

Langkah-langkah berikut menjelaskan siklus proses permintaan kredensial digital dari WebView:

  1. Pembuatan permintaan: Halaman web di WebView membuat permintaan ke server aplikasi, yang menghasilkan JSON permintaan OpenID4VP dan mengirimkannya kembali.
  2. Memulai permintaan: Halaman web menggunakan navigator.credentials.get({ digital: ... }) untuk meminta kredensial digital menggunakan JSON permintaan.
  3. Intersepsi: bridge.js yang dimasukkan akan mengintersepsi panggilan ini, mengekstrak JSON permintaan OpenID4VP tertentu, dan meneruskannya ke aplikasi Android.
  4. Penanganan Android: Aplikasi Android menerima pesan dan memanggil API CredentialManager.getCredential.
  5. Pemilihan pengguna: Credential Manager menampilkan UI yang meminta pengguna untuk membagikan kredensial mereka. Kredensial ditampilkan ke aplikasi Android.
  6. Respons jembatan: Aplikasi Android mengirimkan respons kembali ke halaman web menggunakan jembatan.
  7. Penyelesaian: Halaman web menerima dan menangani respons.

Prasyarat

Sebelum Anda menerapkan jembatan JavaScript untuk kredensial digital di WebView, pastikan project Anda memenuhi persyaratan berikut:

  • Kompatibilitas versi Android: Aplikasi Anda harus menargetkan Android 6.0 (level API 23) atau yang lebih tinggi.
  • Kontrol aplikasi host: Anda harus mengontrol aplikasi Android yang menghosting WebView agar dapat melampirkan WebMessageListener sebelum memuat halaman web.
  • HTTPS dan frame tingkat atas: Halaman web yang meminta kredensial harus ditayangkan melalui https:// dan dijalankan di frame utama (isMainFrame harus true). Subframe dan elemen <iframe> tersemat tidak didukung.
  • Keikutsertaan API eksperimental: Karena Digital Credentials API bersifat eksperimental, Anda harus menganotasi class atau metode yang menggunakan GetDigitalCredentialOption dengan @OptIn(ExperimentalDigitalCredentialApi::class).

Menambahkan dependensi

Tambahkan dependensi Credential Manager dan AndroidX WebKit ke file build.gradle.kts modul aplikasi Anda:

dependencies {
    // Credential Manager dependencies
    implementation("androidx.credentials:credentials:1.7.0-alpha03")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")

    // AndroidX WebKit (required for WebMessageListener bridge)
    implementation("androidx.webkit:webkit:1.14.0")
}

Mengimplementasikan kredensial digital di WebView

Anda harus menambahkan JavaScript ke WebView untuk membuat permintaan kredensial dan menangkapnya dengan bridge.js.

Membuat permintaan JavaScript

Untuk berkomunikasi antara JavaScript yang berjalan di WebView dan kode Android, masukkan kode JavaScript ke WebView Anda:

// Example digital credential request to request verified email
const request = {
  "providers": [
      {
        "protocol": "openid4vp",
        "request": JSON.stringify({
          "requests": [
            {
              "protocol": "openid4vp-v1-unsigned",
              "data": {
                "response_type": "vp_token",
                "response_mode": "dc_api",
                "nonce": "n-0S6_W_2F", // Generate dynamically
                "dcql_query": {
                  "credentials": [
                    {
                      "id": "user_info_query",
                      "format": "dc+sd-jwt",
                      "meta": {
                        "vct_values": ["UserInfoCredential"]
                      },
                      "claims": [
                        {"path": ["email"]},
                        {"path": ["email_verified"]},
                        {"path": ["name"]}
                      ]
                    }
                  ]
                }
              }
            }
          ]
      })
    }
  ]
};

try {
  const credential = await navigator.credentials.get({
    digital: request
  });

  // Process response
} catch (err) {
  // Handle error
}

Membuat jembatan JavaScript

Buat file bernama bridge.js atau masukkan kode ini menggunakan evaluateJavascript. Skrip ini mengganti navigator.credentials.get untuk menangkap permintaan dan mengirim string JSON OpenID4VP internal ke aplikasi Android.

(function() {
  if (!navigator.credentials) {
    navigator.credentials = {};
  }

  const originalGet = navigator.credentials.get;

  // Overrides navigator.credentials.get() to intercept W3C Digital Credential requests.
  navigator.credentials.get = async function(options) {

    if (options && options.digital) {
      if (window.DigitalCredentialBridge) {
        return new Promise((resolve, reject) => {
          const digital = options.digital;

          if (digital.providers && digital.providers.length > 0) {
            const provider = digital.providers[0];

            if (provider.protocol === "openid4vp") {
              const requestJson = provider.request;

              const message = {
                type: "get",
                request: requestJson
              };

              console.log("Sending message to native bridge:", JSON.stringify(message));
              // Send the request string to native Android code
              window.DigitalCredentialBridge.postMessage(JSON.stringify(message));

              // Receive the response sent from native code using replyProxy.postMessage()
              window.DigitalCredentialBridge.onmessage = function(event) {
                console.log("Received response from native bridge:", event.data);
                try {
                  const response = JSON.parse(event.data);
                  if (response.error) {
                    reject(new Error(response.error));
                  } else {
                    resolve(response);
                  }
                } catch (e) {
                  // If it's not JSON, just resolve with the raw data
                  resolve(event.data);
                }
              };
            } else {
              reject(new Error("Unsupported protocol: " + provider.protocol));
            }
          } else {
            reject(new Error("No providers specified in digital credential request"));
          }
        });
      } else {
        console.warn("DigitalCredentialBridge not found, falling back to original");
      }
    }

    if (originalGet) {
      return originalGet.call(navigator.credentials, options);
    } else {
      throw new Error("navigator.credentials.get is not supported and no bridge available");
    }
  };
})();

Mengimplementasikan kredensial digital di Android

Konfigurasikan aplikasi Android Anda untuk menangani permintaan kredensial yang dikirim dari bridge.js.

Membuat pemroses Android

Siapkan class DigitalCredentialWebListener yang mengimplementasikan WebViewCompat.WebMessageListener. Class ini menangani pesan dua arah antara JavaScript dan kode Android native, menerapkan pemeriksaan keamanan pada isMainFrame dan sourceOrigin, serta mencegah permintaan yang tumpang-tindih.

Mengimplementasikan Pengendali Credential Manager

Panggil getCredential untuk meminta kredensial digital dari perangkat.

@OptIn(ExperimentalDigitalCredentialApi::class)
class CredentialManagerHandler(private val context: Context) {
  private val credentialManager = CredentialManager.create(context)

  suspend fun getDigitalCredential(requestJson: String): String {
    val digitalCredentialOption = GetDigitalCredentialOption(requestJson)
    val getCredRequest = GetCredentialRequest(listOf(digitalCredentialOption))

    return try {
      val result = credentialManager.getCredential(context, getCredRequest)
      val credential = result.credential
      if (credential is DigitalCredential) {
        credential.credentialJson
      } else {
        "{\"error\": \"Unexpected credential type\"}"
      }
    } catch (e: GetCredentialException) {
      "{\"error\": \"${e.message}\"}"
    } catch (e: Exception) {
      "{\"error\": \"Unknown exception\"}"
    }
  }

  companion object {
    private const val TAG = "CredManHandler"
  }
}

Menerapkan framework ke Aktivitas Anda

Di Activity atau View, daftarkan WebMessageListener dan fungsi terkait.

// Inside your Activity or View setup
AndroidView(
  factory = { context ->
    val handler = CredentialManagerHandler(context)
    WebView(context).apply {
      settings.javaScriptEnabled = true

      // Register the bridge before loading URL
      if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
                val listener = DigitalCredentialWebListener(coroutineScope, credentialManagerHandler)
                WebViewCompat.addWebMessageListener(
                    webView,
                    INTERFACE_NAME,
                    setOf("*"),
                    listener
                )


      loadUrl("https://your-secure-domain.com/login")
    }
  },
)

Menguji alur

Uji alur untuk mengonfirmasi komunikasi antara WebView dan aplikasi Android. Pemilih kredensial akan muncul di WebView selama proses berlangsung, dan kredensial yang dipilih akan ditampilkan ke WebView setelah selesai.