Accepting IDs from Google Wallet

Online

Digital IDs can be accepted both in-app and web flows. To accept credentials from Google Wallet you will need to:

  1. Integrate using app or web following the provided instructions and
  2. Fill out this form to request and agree to the terms of service of accepting credentials from Google Wallet.

Prerequisites

To test presentation of IDs, you must first enroll in the public beta program using the intended test account. Subsequently, furnish the ensuing details to your designated Google contact.

  • Terms of Service link
  • Logo
  • Website
  • Play Store package ID (for Android app integrations)
  • Gmail ID that was used to join the public beta

Supported Credential Formats

Several proposed standards exist that define the data format of digital identity documents, with two gaining significant industry traction:

  1. mdocs - defined by ISO.
  2. W3C Verifiable Credentials - defined by the W3C.

While the Android Credential Manager supports both the formats, Google Wallet supports only mdoc based Digital IDs at the moment.

User experience

When an application requests identity attributes, the following process occurs:

  1. Credential Discovery: The application queries available wallets to identify credentials that can satisfy the request. Android then presents a system UI selector, displaying the information to be shared. This allows the user to make an informed decision about which credential to use.

  2. User Selection and Wallet Interaction: The user selects a credential, and Android invokes the corresponding wallet app to complete the transaction. The wallet app may present its own consent screen or require biometric confirmation.

Outcome: If the user consents, the selected identity credentials are shared with the requesting application. If the user declines, an error is returned.

In App

To request identity credentials from your Android apps follow these steps:

Update dependencies

In your project's build.gradle, update your dependencies to use Credential Manager (beta):

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    // optional - needed for credentials support from play services, for devices running Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

Configure the Credential Manager

To configure and initialize a CredentialManager object, add logic similar to the following:

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

Request Identity attributes

Instead of specifying individual parameters for identity requests, the app provides them all together as a JSON string within the CredentialOption. The Credential Manager passes this JSON string along to the available digital wallets without examining its contents. Each wallet is then responsible for: - Parsing the JSON string to understand the identity request. - Determining which of its stored credentials, if any, satisfy the request.

It's expected that the W3C will formally define the structure of this JSON request as part of the web API specification. However, it's important to remember that the specification is in draft form and subject to change.

Initially, we will be utilizing the preview protocol to obtain IDs from Google Wallet. This allows us to begin integration and testing while the web API specification is finalized.

Here is a sample of an mdoc requestJson for the preview protocol:

{
  identity: {
    providers: [{
      holder: {
        selector: {
          format: ['mdoc'],
          type: 'org.iso.18013.5.1.mDL',
          fields: [
            'org.iso.18013.5.1.family_name',
            'org.iso.18013.5.1.portrait',
          ]
        },
        params: {
          nonce: '1234',
          readerPublicKey: '<public_key>',
          extraParamAsNeededByWallets: true,
        },
      },
    }]
  }
}
// The request in the JSON format to conform with
// the JSON-ified Digital Credentials 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)
)

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

The response returns an identityToken (json string), defined by the W3C. The Wallet app is responsible for crafting this response.

Example:

{
    "token": "<base64 encoded response>"
}

Send token and process on the server

Upon receiving the identityToken, your application should transmit it to your application server for verification. The initial step involves decoding the token from base64 format. The resulting byte array represents the CBOR data, which adheres to the following CDDL.

CredentialDocument = {
  "version": tstr,       // Set to "ANDROID-HPKE-v1"
  "pkEm": bstr,          // Public key, in uncompressed form
  "cipherText": bstr     // The encrypted data
}

The next step is to calculate the SessionTranscript from ISO/IEC 18013-5:2021 with an Android-specific Handover structure:

SessionTranscript = [
  null,                // DeviceEngagementBytes not available
  null,                // EReaderKeyBytes not available
  AndroidHandover      // Defined below
]

AndroidHandover = [
  "AndroidHandoverv1", // Version number
  nonce,               // nonce that comes from request
  appId,               // RP package name
  pkRHash,             // The SHA256 hash of the recipient public key
]

The cipherText is encrypted using HPKE encryption. To decrypt it, use SessionTranscript as the Additional Authenticated Data, along with the EC private key that was generated previously, and the following settings:

  • KEM: DHKEM(P-256, HKDF-SHA256)
  • KDF: HKDF-SHA256
  • AEAD: AES-128-GCM

The resulting cleartext is the DeviceResponse CBOR bytes as defined in ISO/IEC 18013-5:2021. DeviceResponse must be validated according to ISO/IEC 18013-5:2021 clause 9. This includes several steps, such as verifying that the mdoc originates from a trusted issuer and that the response is signed by the intended device. The DeviceResponseParser class from the OpenWallet Foundation Identity Credential project can be used for part of this validation process.

Web

To request Identity Credentials using the Digital Credentials API on Chrome, you'll need to sign up for the Digital Credentials API origin trial.

In Person

Accepting IDs from Google Wallet requires the following steps:

  • Build or acquire a reader to accept IDs as defined by ISO 18013-5
  • Load IACA certificates into the reader to ensure accepted IDs are authentic
  • Test your solution
  • Register your application with Google Wallet

Build or acquire a reader to accept IDs as defined by ISO 18013-5

IDs in Wallet are implemented according to the ISO 18013-5 standard for mobile drivers licenses. They use NFC-based or QR code engagement along with BLE as the data transfer mechanism - so any device which can implement those aspects of the standard can act as a reader, even a mobile application. As the standard is open there are several 3rd party implementations available on the market. As well, you can implement the functionality directly if needed.

For guidance on how to implement the functionality yourself, see our open source reference reader Android app, which implements the ISO standard and can accept mDLs from Google Wallet.

You can get started by building and running the reference reader app:

  • Clone the reference apps repository
  • Open the project on Android Studio
  • Build and run the appverifier target on your Android device or emulator.

Load IACA certificates into the reader to ensure accepted IDs are authentic

Validating a real credential requires you to have an ID in wallet from a supported issuer. A list of issuers that are supported by Google Wallet is provided below along with links to their certificates for verification.

To test your solution, build and run our open source reference holder Android application. Here are the steps to build and run the reference holder app:

  • Clone the reference apps repository
  • Open the project on Android Studio
  • Build and run the appholder target on your Android device or emulator.

(Optional) Register your application with Google Wallet

Register your application with Google Wallet by filling up this form.