Poproś o cyfrowe poświadczenie

Weryfikacja cyfrowych dokumentów tożsamości w aplikacjach na Androida może służyć do uwierzytelniania i autoryzacji tożsamości użytkownika (np. dokumentu tożsamości), jego cech (np. prawa jazdy, stopnia naukowego lub atrybutów takich jak wiek czy adres) lub w innych sytuacjach, w których dokument tożsamości musi zostać wydany i zweryfikowany, aby potwierdzić autentyczność podmiotu.

Interfejs Verifier API jest obsługiwany na Androidzie 9 (poziom interfejsu API 28) i nowszych wersjach.

Implementacja

Aby zweryfikować cyfrowe dokumenty tożsamości w projekcie aplikacji na Androida, wykonaj te czynności:

  1. Dodaj zależności do skryptu kompilacji aplikacji i zainicjuj klasę CredentialManager.
  2. Utwórz żądanie cyfrowego dokumentu tożsamości i użyj go do zainicjowania DigitalCredentialOption, a następnie utwórz GetCredentialRequest.
  3. Uruchom proces getCredential z utworzonym żądaniem, aby otrzymać prawidłowy obiekt GetCredentialResponse lub obsłużyć ewentualne wyjątki. Po pomyślnym pobraniu sprawdź odpowiedź.

Dodawanie zależności i inicjowanie

Dodaj do skryptu kompilacji Gradle te zależności:

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

Następnie zainicjuj instancję klasy CredentialManager.

val credentialManager = CredentialManager.create(context)

Tworzenie żądania cyfrowego dokumentu tożsamości

Utwórz żądanie cyfrowego dokumentu tożsamości i użyj go do zainicjowania 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)
)

Poniższy przykład pokazuje żądanie OpenId4Vp1. Szczegółowe informacje o polach w tym żądaniu znajdziesz w dokumentacji 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"
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

Uzyskiwanie danych logowania

Uruchom proces getCredential z utworzonym żądaniem. Otrzymasz komunikat o powodzeniu GetCredentialResponse lub komunikat o błędzie GetCredentialException, jeśli żądanie się nie powiedzie.

Proces getCredential wywołuje okna systemowe Androida, aby wyświetlić użytkownikowi dostępne opcje danych logowania i zebrać jego wybór. Następnie aplikacja portfela, która zawiera wybraną opcję dokumentu, wyświetli interfejsy, aby uzyskać zgodę i wykonać wymagane działania potrzebne do wygenerowania odpowiedzi dotyczącej dokumentu cyfrowego.

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}")
    }
}
Obraz przedstawiający selektor wyświetlanych cyfrowych dokumentów tożsamości podczas wywoływania metody getCredential.
Rysunek 1. Wyświetlany selektor danych cyfrowych podczas wywoływania funkcji getCredential.

Elastyczne żądania dotyczące wielu danych logowania

Interfejs Digital Credentials API obsługuje też żądanie wielu typów danych uwierzytelniających jednocześnie za pomocą OpenID4VP:

  • Jednoczesne żądania: żądanie wielu danych logowania w ramach jednej transakcji użytkownika. Na przykład aplikacja do podróży może jednocześnie poprosić o kartę pokładową i dokument tożsamości, dzięki czemu użytkownik może sprawdzić oba dokumenty i wyrazić na nie zgodę jednym kliknięciem.
  • Elastyczna realizacja: akceptuj dowolny z kilku typów danych uwierzytelniających lub wydawców. Na przykład proces weryfikacji wieku może akceptować prawo jazdy lub paszport, umożliwiając użytkownikom spełnienie wymagań za pomocą dowolnego dokumentu tożsamości zapisanego w aplikacji posiadacza.

Oto przykład żądania wielu danych logowania w jednym żądaniu:

{
  "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"
          ]
        }
      ]
    }
  ]
}
Obraz przedstawiający selektor cyfrowych dokumentów tożsamości wyświetlany podczas wywoływania wielu typów dokumentów.
Rysunek 1. Wyświetlany selektor cyfrowych danych logowania podczas wywoływania wielu typów danych logowania.

Obsługa odpowiedzi

Po wysłaniu żądania danych logowania obsłuż obiekt GetCredentialResponse zwrócony przez 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}")
        }
    }
}

Obsługa wyjątków

Aplikacja powinna obsługiwać błędy, które mogą wystąpić, w tym brak danych logowania na urządzeniu lub anulowanie operacji przez użytkownika:

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