Skip to content

HOWTO: Try the Prototype API in Chrome Android

Rick Byers edited this page May 7, 2024 · 13 revisions

Identity Credentials Quick Start Guide

Sept 2023

This HOWTO will guide you through to with with the prototype of the API in Chrome and Android. It is a bit cumbersome at the moment because it is in its early stage of incubation (e.g. protected by flags). If you run into problems, feel free to ping @leecam or @samuelgoto on slack.

Test device setup

  1. Ensure the Identity Credential API is enabled on Android

    1. Ensure Google Play Services is at least version 23.40 or greater. This can be found by going to do Settings->All apps->Google Play Services
    2. Note it is no longer necessary to enroll your Android device in the Google Play Services Beta program
  2. Enable the Identity Credential API in Chrome

    1. Install the chrome dev build https://play.google.com/store/apps/details?id=com.chrome.dev&hl=en_US&gl=US
    2. Open chrome and go to chrome://flags
    3. Search for digital credentials and enable that flag, and hit "relaunch".

Testing the setup

You should now be able to test the Identity Credential with our sample apps to verify your setup:

  1. Install the sample wallet app here. It will appear in the launcher as IC Purse. This is the Open Wallet Foundation reference wallet which can also be built directly from source if you prefer.
adb install -t <path-to-apk>
  1. Install the sample reader app here. It will appear in the launcher as App Verifier. This is the Open Wallet Foundation reference verifier app.
adb install -t <path-to-apk>
  1. Launch the App Holder app and provision a new mDL
    1. Tap the menu button and select Add Self Signed Document
    2. This document will now be available for presentment to native apps and websites
  2. Launch the App Verifier app and tap Request via Credential Manager. This should invoke the Credential Selector UX showing the available documents that match the request. At this point you see the mDL you provisioned above.
  3. Select the mDL. The Verifier app will now show the information it received.
  4. You can also test via the web.
    1. Navigate Chrome Canary to www.identitycredential.dev and ensure you also request the mDL successfully.

You can use this website and reader app to test and verify your wallet application. You should see your credentials alongside credentials from our sample wallet.

If you got this far, you should have something that looks more or less like the following:

https://www.youtube.com/watch?v=mZeSVNK0jlw

Verifier API

You can build a website that uses the JS API to request documents on the web.

// Gets a CBOR with specific fields out of mobile driver's license as an mdoc
const controller = new AbortController();
const {response} = await navigator.identity.get({
    signal: controller,
    digital: {
      providers: [{
        protocol: "basic",
        request: JSON.stringify({
          selector: {
            format: ["mdoc"],
            retention: {days: 90},
            doctype: "org.iso.18013.5.1.mDL",
            fields: [
              "org.iso.18013.5.1.document_number",
              "org.iso.18013.5.1.portrait",
              "org.iso.18013.5.1.driving_privileges",
              "org.iso.18013.5.1.aamva.organ_donor",
            ],
          },
          nonce: "gf69kepV+m5tGxUIsFtLi6pwg=",
          // TODO: maybe move this out of request and into publicKey?
          readerPublicKey: "ftl+VEHPB17r2 ... Nioc9QZ7X/6w...",
        }
      })],
    }
});

You can also build a native app verifier by calling the following API:

TODO(@leecam): write the instructions on how to use the CredMan API for requesting digital credentials

Holder API

Building the sample wallet app

You can build our samples as a starting point and have a play.

  1. Setup a local maven repo. Download the local maven repo from here
# cd ~/.m2/repository
# unzip idsdk.zip
  1. Check out the sample code The code for the sample apps is here https://github.com/google/identity-credential/tree/android-credential-manager Make sure you use the android-credential-manager branch
  2. Load the project in Android Studio
  3. You can build the App Holder app and install it on your device

You should now be able to use the use the demo apps as before

Setting up the SDK in your app

Note: Remember to only use the two app package names you shared with us. We allow-listed them to use this API while the API is still under development so that we can control backwards incompatible breaking changes.

  1. You need to add the local maven repo to your settings.gradle file.
  2. You can do this by adding mavenLocal() to the repositories section.
  3. Next add the SDK dependency to your build.gradle.
 implementation 'com.google.android.gms:play-services-identity-credentials:0.0.1-eap01'

e.g https://github.com/google/identity-credential/blob/android-credential-manager/appholder/build.gradle#L80

Where to look in the sample wallet

The credential registration happens here: https://github.com/google/identity-credential/blob/android-credential-manager/appholder/src/main/java/com/android/mdl/app/document/DocumentManager.kt#L88

The Provider API

Note: This API will be provided as part of the Credential Manager Jetpack Library. Unfortunately we can’t share this with you directly quite yet. Instead you’ll use some slightly lower-level APIs. Jetpack just provides more developer friendly wrappers over the API you’ll be using today. It's still fairly straightforward but note that when this API is released the public API will be exposed via Cred Man.

The incoming request parameters are provided to your wallet app as a JSON string. This JSON string is provided by the calling RP application. The specification of this JSON is currently being defined by the W3C, but this API doesn’t concern itself with its contents. It is the responsibility of your wallet app to parse this request and form the response.

Chrome and our test apps provide the JSON in the following form. This is just a simple request format to demonstrate the API, this will likely evolve in the W3C working group. This is the RedBox in David’s ISO presentation.

{
  "providers": [{
    "protocol": "basic",
    "request": "{\"selector\":{\"format\":[\"mdoc\"] ... c9QZ7X/6w...\"}}"
  }]
}

The provider API allows you to define the matching logic used by your wallet to decide which documents/credentials to show in Credential Selection UI for a given json request.

This matcher logic is defined as a wasm module that you register with the system as follows

val registrationRequest = RegistrationRequest(
      credentials = yourMetaData,  // A binary blob that we pass to your matcher
      matcher = yourMatcherBinary, // The wasm module 
      type = "com.credman.IdentityCredential" // has to set to this
    )

val client = IdentityCredentialManager.Companion.getClient(context)
client.registerCredentials(registrationRequest)

Android will execute your wasm matcher in a sandbox upon receiving a request from an RP application or website. The matcher binary will be provided with the credential data blob you provide as part of registration, the incoming request json from the calling RP and the calling app information (calling packagename or origin). The matcher's job is to parse the incoming request and to populate the entries in the selector UX.

As per above, we will provide more developer friendly APIs in jetpack towards the end of the year. This includes default matchers and helper classes. So most wallets won’t need to deal with writing their own matcher unless they have some complex matching logic or want to support a new credential type.

For this proof of concept you can use the matcher from our demo app. You can place it in your assets folder in your app. You can find it here: https://github.com/google/identity-credential/tree/android-credential-manager/appholder/src/main/assets

There are 3 helper classes that you can copy and paste into your wallet app, they can be found here: https://github.com/google/identity-credential/tree/android-credential-manager/appholder/src/main/java/com/android/mdl/app/credman

These helpers use the provided matcher and build up the credential data in a structure the matcher understands.

You can use these helpers to register a simple credential as follows:

val fields = mutableListOf<IdentityCredentialField>()
// Add the doc type field
fields.add(IdentityCredentialField(
name = "doctype",
       value = "fakedoc",
       displayName = "Document Type",
       displayValue = "Fake doc type"
))

// Add a name field
fields.add(IdentityCredentialField(
name = "firstname",  // field name is required for matching the fields in the json
       value = "Erika",  // the vaule is optional.
       displayName = "First Name", // required to show the matched fields in the selector
       displayValue = "Erika"  // the vaule is optional.
))

// Create the entry
val entry = listOf(IdentityCredentialEntry(
id = 1, // will be passed to your app if the credential is picked by the user
       format = "mdoc",
                title = "Erika's's Driving License",
                subtitle = "California DMV",
                icon = BitmapFactory.decodeResource(context.resources, R.mylogo),
                fields = fields.toList(),
                disclaimer = null,
                warning = null,
 ))

// Create the registry with the list of entries
val registry = IdentityCredentialRegistry(listOf(entry))
// Register using the stock matcher 
val client = IdentityCredentialManager.Companion.getClient(context)
client.registerCredentials(registry.toRegistrationRequest(context))

Once you implement the above registration flow, you can test the web app, your credentials should appear in the selector (assuming they have the required fields to be considered a match).

Invocation

This API attempts to provide a huge amount of flexibility for the wallet application. The goal is to just handle credential selection and wallet invocation. All Android requires is that the wallet (via the matcher) provides enough information about the credential and the requested attributes that we can render a selector. This information allows the user to make an informed choice about which document to proceed with.

Once a credential is selected by the user Android will intent into the wallet app, where it can show its own UI to gather consent from the user, e.g by showing a biometric prompt. Our sample app doesn’t show any UI, but we suggest your app shows at least a biometric prompt.

You need to add a new Activity to your app with the following intent handler in the manifest com.google.android.gms.identitycredentials.action.GET_CREDENTIALS

Here is an example:

<activity
            android:label="@string/app_name"
            android:name=".GetCredentialActivity"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.identitycredentials.action.GET_CREDENTIALS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

Android will invoke this Activity if your credential is selected by the user. You should use it to obtain user consent and form the response.

Again helper libs will do most of this heavy lifting in the future but for now you’ll be exposed to a bit of the plumbing.

Our sample Activity is here: https://github.com/google/identity-credential/blob/android-credential-manager/appholder/src/main/java/com/android/mdl/app/GetCredentialActivity.kt

In your onCreate method you should obtain the request:

// The JSON from the calling app
val request = extractGetCredentialRequest(intent)

// the credential ID of the selected credential (registered above)
val credentialId = intent.getLongExtra(EXTRA_CREDENTIAL_ID, -1)

// The calling app info
val callingAppInfo = extractCallingAppInfo(intent)

You should parse the request and generate the response for the selected credential.

The response is provided as a ByteArray. Our demo apps place a base64 encoded string of encrypted response in this byte array. (at some point we’ll change this to a string)

This string is passed directly back to the calling app. Again Android does not concern itself with the format of the request or the response. We leave it to the wallet to understand the format of the request and generate the response.

The response is provided as follows (again helpers in the future will hide some of these gory details):

val bundle = Bundle()
// you need to generate the encodedCredentialDocument
bundle.putByteArray("identityToken", Base64.encodeToString(encodedCredentialDocument, Base64.NO_WRAP or Base64.URL_SAFE).toByteArray())
val credentialResponse = com.google.android.gms.identitycredentials.Credential("type", bundle)
val response = GetCredentialResponse(credentialResponse)
val resultData = Intent()
setGetCredentialResponse(resultData, response)
setResult(RESULT_OK, resultData)
finish()

How to generate the response

The best way is probably to look at the sample code :)

The logic starts here: https://github.com/google/identity-credential/blob/android-credential-manager/appholder/src/main/java/com/android/mdl/app/GetCredentialActivity.kt#L151

Much of the heavy lifting is performed in this class: https://github.com/google/identity-credential/blob/android-credential-manager/identity-android/src/main/java/com/android/identity/android/mdoc/util/CredmanUtil.kt

Its a standard mdoc device response CBOR, encrypted with HPKE. The main change is the session transcript, which is generated here: https://github.com/google/identity-credential/blob/android-credential-manager/appholder/src/main/java/com/android/mdl/app/GetCredentialActivity.kt#L158

Note: We always set the calling package name to "com.android.mdl.appreader" in our sample apps, so you’ll need to do this too until we fix this hack.