Solicita una credencial digital

La verificación de credenciales digitales en las apps para Android se puede usar para autenticar y autorizar la identidad de un usuario (como un documento de identidad emitido por el Gobierno), propiedades sobre ese usuario (como una licencia de conducir, un título académico o atributos como la edad o la dirección) o en otras situaciones en las que se deba emitir y verificar una credencial para confirmar la autenticidad de una entidad.

La API de Verifier es compatible con Android 9 (nivel de API 28) y versiones posteriores.

Implementación

Para verificar las credenciales digitales en tu proyecto de Android, haz lo siguiente:

  1. Agrega dependencias a la secuencia de comandos de compilación de tu app y, luego, inicializa una clase CredentialManager.
  2. Crea una solicitud de credencial digital y úsala para inicializar un objeto DigitalCredentialOption y, luego, compila el objeto GetCredentialRequest.
  3. Inicia el flujo de getCredential con la solicitud construida para recibir un GetCredentialResponse correcto o controlar las excepciones que puedan ocurrir. Una vez que se recupere correctamente, valida la respuesta.

Agrega dependencias y, luego, inicializa

Agrega las siguientes dependencias a tu secuencia de comandos de compilación de Gradle:

dependencies {
    implementation("androidx.credentials:credentials:1.6.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta01")
}

A continuación, inicializa una instancia de la clase CredentialManager.

val credentialManager = CredentialManager.create(context)

Crea una solicitud de credencial digital

Crea una solicitud de credencial digital y úsala para inicializar un objeto DigitalCredentialOption.

// The request in the JSON format to conform with
// the JSON-ified Credential Manager - Verifier API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

En el siguiente ejemplo, se muestra una solicitud de OpenId4Vp1. Para obtener detalles sobre los campos de esta solicitud, consulta la documentación de OpenID.

{
  "requests": [
    {
      "protocol": "openid4vp-v1-unsigned",
      "data": {
        "response_type": "vp_token",
        "response_mode": "dc_api",
        "nonce": "OD8eP8BYfr0zyhgq4QCVEGN3m7C1Ht_No9H5fG5KJFk",
        "dcql_query": {
          "credentials": [
            {
              "id": "cred1",
              "format": "mso_mdoc",
              "meta": {
                "doctype_value": "org.iso.18013.5.1.mDL"
              },
              "claims": [
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "family_name"
                  ]
                },
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "given_name"
                  ]
                },
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "age_over_21"
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

Obtén la credencial

Inicia el flujo de getCredential con la solicitud construida. Recibirás un GetCredentialResponse correcto o un GetCredentialException si la solicitud falla.

El flujo getCredential activa los diálogos del sistema Android para presentar las opciones de credenciales disponibles del usuario y recopilar su selección. A continuación, la app de billetera que contiene la opción de credencial elegida mostrará las IU para recopilar el consentimiento y realizar las acciones necesarias para generar una respuesta de credencial digital.

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson)
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}
Imagen que muestra el selector de credenciales digitales que se muestra cuando se llama a getCredential.
Figura 1. Selector de credenciales digitales que se muestra cuando se llama a getCredential.

Solicitudes flexibles de varias credenciales

La API de Digital Credentials también admite la solicitud de varios tipos de credenciales a la vez con OpenID4VP:

  • Solicitudes simultáneas: Solicita varias credenciales en una sola transacción del usuario. Por ejemplo, una app de viajes puede solicitar una tarjeta de embarque y un documento de identidad emitido por el Gobierno de forma simultánea, lo que permite que el usuario revise y otorgue su consentimiento para ambos con un solo toque.
  • Cumplimiento flexible: Acepta cualquiera de los distintos tipos de credenciales o emisores. Por ejemplo, un flujo de verificación de edad puede aceptar una licencia de conducir o un pasaporte, lo que permite que los usuarios completen la solicitud con la credencial que tengan almacenada en su billetera.

Este es un ejemplo de cómo solicitar varias credenciales en una sola solicitud:

{
  "credentials": [
    {
      "id": "gov_id1",
      "format": "mso_mdoc",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
      },
      "claims": [
        {
          "path": [
            "org.iso.18013.5.1",
            "age_over_21"
          ]
        }
      ]
    },
    {
      "id": "boarding_pass1",
      "format": "dc+sd-jwt",
      "meta": {
        "vct_values": [
          "https://credentials.example.airline/boarding-pass"
        ]
      },
      "claims": [
        {
          "path": [
            "flight_number"
          ]
        },
        {
          "path": [
            "seat"
          ]
        }
      ]
    }
  ]
}
Imagen que muestra el selector de credenciales digitales que se muestra cuando se llaman varios tipos de credenciales.
Figura 1. Selector de credenciales digitales que se muestra cuando se llaman varios tipos de credenciales

Cómo controlar la respuesta

Después de realizar la solicitud de credenciales, controla el GetCredentialResponse que se muestra desde CredentialManager.

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson)
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

Controla excepciones

Tu app debería controlar los errores que se pueden generar, incluso si no hay credenciales en el dispositivo o si el usuario cancela la operación:

// Handle failure.
fun handleFailure(e: GetCredentialException) {
    when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        is CreateCredentialUnknownException -> {
            // An unknown, usually unexpected, error has occurred. Check the
            // message error for any additional debugging information.
        }
        is CreateCredentialCustomException -> {
            // You have encountered a custom error thrown by the wallet.
            // If you made the API call with a request object that's a
            // subclass of CreateCustomCredentialRequest using a 3rd-party SDK,
            // then you should check for any custom exception type constants
            // within that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}