Demander des identifiants numériques dans WebView

Ce guide explique comment demander des identifiants numériques à l'aide de Credential Manager dans une application Android qui utilise WebView. Utilisez un pont JavaScript pour faciliter la communication entre la page Web et l'application.

Présentation

Les étapes suivantes décrivent le cycle de vie d'une demande d'identifiant numérique à partir d'une WebView :

  1. Génération de la requête : la page Web de la WebView envoie une requête au serveur d'application, qui génère un fichier JSON de requête OpenID4VP et le renvoie.
  2. Lancement de la requête : la page Web utilise navigator.credentials.get({ digital: ... }) pour demander des identifiants numériques à l'aide du fichier JSON de la requête.
  3. Interception : le bridge.js injecté intercepte cet appel, extrait le fichier JSON de la requête OpenID4VP spécifique et le transmet à l'application Android.
  4. Gestion Android : l'application Android reçoit le message et appelle l'API CredentialManager.getCredential.
  5. Sélection de l'utilisateur : Credential Manager affiche une UI invitant l'utilisateur à partager ses identifiants. Les identifiants sont renvoyés à l'application Android.
  6. Réponse du pont : l'application Android renvoie la réponse à la page Web à l'aide du pont.
  7. Finalisation : la page Web reçoit et traite la réponse.

Prérequis

Avant d'implémenter le pont JavaScript pour les identifiants numériques dans WebView, assurez-vous que votre projet répond aux exigences suivantes :

  • Compatibilité avec les versions d'Android : votre application doit cibler Android 6.0 (niveau d'API 23) ou version ultérieure.
  • Contrôle de l'application hôte : vous devez contrôler l'application Android qui héberge le WebView afin de pouvoir associer un WebMessageListener avant de charger la page Web.
  • HTTPS et frame de niveau supérieur : la page Web demandant l'identifiant doit être diffusée via https:// et s'exécuter dans le frame principal (isMainFrame doit être true). Les sous-frames et les éléments <iframe> intégrés ne sont pas acceptés.
  • Activation de l'API expérimentale : l'API Digital Credentials étant expérimentale, vous devez annoter les classes ou les méthodes qui utilisent GetDigitalCredentialOption avec @OptIn(ExperimentalDigitalCredentialApi::class).

Ajouter des dépendances

Ajoutez les dépendances Credential Manager et AndroidX WebKit au fichier build.gradle.kts de votre module d'application :

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")
}

Implémenter des identifiants numériques dans WebView

Vous devez ajouter du code JavaScript à votre WebView pour envoyer la demande d'identifiants et l'intercepter avec bridge.js.

Créer la requête JavaScript

Pour communiquer entre le code JavaScript exécuté dans une WebView et le code Android, injectez du code JavaScript dans votre WebView :

// 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
}

Créer le pont JavaScript

Créez un fichier nommé bridge.js ou injectez ce code à l'aide de evaluateJavascript. Ce script remplace navigator.credentials.get pour intercepter la requête et envoyer la chaîne JSON OpenID4VP interne à l'application 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");
    }
  };
})();

Implémenter des identifiants numériques dans Android

Configurez votre application Android pour qu'elle gère les demandes d'identifiants envoyées par bridge.js.

Créer l'écouteur Android

Configurez une classe DigitalCredentialWebListener qui implémente WebViewCompat.WebMessageListener. Cette classe gère les messages bidirectionnels entre JavaScript et le code Android natif, en appliquant des contrôles de sécurité sur isMainFrame et sourceOrigin, et en empêchant les requêtes qui se chevauchent.

Implémenter le gestionnaire Credential Manager

Appelez getCredential pour demander des identifiants numériques à l'appareil.

@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"
  }
}

Appliquer le framework à votre activité

Dans votre Activity ou View, enregistrez le WebMessageListener et les fonctions associées.

// 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")
    }
  },
)

Test du flux…

Testez le flux pour confirmer la communication entre la WebView et l'application Android. Le sélecteur d'identifiants s'affiche dans la WebView pendant le processus, et l'identifiant sélectionné est renvoyé à la WebView une fois l'opération terminée.