GuidesAndroid mDocs Holder SDK🎓 Online presentation

Learn how to build an Android application that can present an mDoc online

Introduction

In this tutorial we will use the Android native mDoc Holder SDK to build an Android application that can present a claimed mDoc to a verifier remotely via an online presentation workflow as per ISO/IEC 18013-7:2024 and OID4VP.

This app will support both same-device and cross-device workflows to accommodate flexible user journeys.

Same-device workflow

Tutorial Workflow

  1. The user interacts with a website on their mobile device browser.
  2. The user is asked to present information as part of the interaction.
  3. The user is redirected to the application we will build in this tutorial.
  4. The application authenticates the user.
  5. The user is informed of what information they are about to share and provide their consent.
  6. The user is redirected back to the browser where verification results are displayed, enabling them to continue with the interaction.

The result will look something like this:

Cross-device workflow

Tutorial Workflow

  1. The user interacts with a website on their desktop browser.
  2. The user is asked to present information as part of the interaction.
  3. The user scans a QR code using a mobile device where the tutorial application is installed.
  4. The tutorial application is launched on the mobile device.
  5. The tutorial application authenticates the user.
  6. The user is informed of what information they are about to share and provide their consent.
  7. Verification results are displayed in the user’s desktop browser, enabling them to continue with the interaction.

The result will look something like this:

Prerequisites

Before we get started, let’s make sure you have everything you need.

Prior knowledge

If you need to get a holding solution up and running quickly with minimal development resources and in-house domain expertise, talk to us about our white-label MATTR GO Hold app which might be a good fit for you.

Assets

As part of your onboarding process you should have been provided with access to the following assets:

  • ZIP file which includes the required library: (holder-*version*.zip).
  • Sample Wallet app: You can use this app for reference as we work through this tutorial.

This tutorial is only meant to be used with the most recent version of the Android mDocs Holder SDK.

Development environment

Prerequisite tutorial

  • You must complete the Claim a credential tutorial and claim the mDoc provided in the tutorial.
  • This application is used as the base for the current tutorial.

Testing devices

  • Supported Android device to run the built application on, setup with:
    • Biometric authentication (Face recognition, fingerprint recognition).
    • Bluetooth access and Bluetooth turned on.
    • Available internet connection.
    • USB debugging enabled.

Tutorial steps

To enable a user to present a stored mDoc to a verifier via an online presentation workflow, you will build the following capabilities into your application:

  1. Register the verifier’s Authorization endpoint.
  2. Create an online presentation session.
  3. Handle a presentation request.
  4. Send a presentation response.

Register the verifier’s Authorization endpoint

The Authorization endpoint is a URI associated with an application identifier in the MATTR VII tenant configuration. It is used to invoke an application that will handle the presentation request. The application then uses the URI to retrieve a request object, which details what information is required for verification.

Online verifiers are recommended to generate this URI as an App Link, as this enables them to explicitly define and validate applications that can respond to their verification requests.

However, for simplicity reasons in this tutorial our verifier is using a deep link with the default custom URI scheme, defined by the OID4VP specification (mdoc-openid4vp). This means that we need to configure the application to be able to handle this custom URI scheme.

  1. Open the Android Studio project with the application built in the Claim a credential tutorial.

  2. Open your AndroidManifest.xml.

  3. Add the following intent filter to your activity object:

    AndroidManifest.xml
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
     
        <data android:scheme="mdoc-openid4vp" />
    </intent-filter>
  4. Run the app and then close it (this updates the app on your testing device) and perform the following instructions:

    • Use a desktop browser to navigate to the MATTR Labs Maggie’s Groceries demo, where you must provide proof for purchasing an age restricted item.
    • In the checkout area, use the dropdown list to select Generic Wallet.
    • Select Share from wallet.
    • Open the camera on your testing mobile device and scan the QR code (or use a specific QR scanning tool, if your device doesn’t support scanning a QR right from the camera app).
    • Confirm opening the QR code with your tutorial application.
    • The tutorial application should be launched on your testing mobile device.

Create an online presentation session

Now that the application can handle an OID4VP custom URI scheme, the next step is build the capability to use the request URI to retrieve the request object, which includes the following information:

  • What credentials are required.
  • What specific claims are required from these credentials.
  • What MATTR VII tenant to interact with.
  1. Create a new file named OnlinePresentationScreen.kt and add the following code to the file:

    OnlinePresentationScreen.kt
    import android.app.Activity
    import androidx.compose.foundation.border
    import androidx.compose.foundation.clickable
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.Spacer
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.padding
    import androidx.compose.foundation.rememberScrollState
    import androidx.compose.foundation.shape.RoundedCornerShape
    import androidx.compose.foundation.verticalScroll
    import androidx.compose.material3.Button
    import androidx.compose.material3.Card
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.LaunchedEffect
    import androidx.compose.runtime.getValue
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.remember
    import androidx.compose.runtime.rememberCoroutineScope
    import androidx.compose.runtime.setValue
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.unit.dp
    import global.mattr.mobilecredential.common.deviceretrieval.devicerequest.DataElements
    import global.mattr.mobilecredential.common.deviceretrieval.deviceresponse.NameSpace
    import global.mattr.mobilecredential.common.dto.MobileCredential
    import global.mattr.mobilecredential.common.dto.MobileCredentialMetaData
    import global.mattr.mobilecredential.holder.MobileCredentialHolder
    import global.mattr.mobilecredential.holder.onlinepresentation.OnlinePresentationSession
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
     
    @Composable
    fun OnlinePresentationScreen(activity: Activity, requestUri: String) {
        var session: OnlinePresentationSession? by remember { mutableStateOf(null) }
     
        // Step 2.1: Create an online presentation session
     
        val (requested, matched) = session?.matchedCredentials?.entries?.firstOrNull() ?: return
     
        var matchedCredentials by remember { mutableStateOf(matched) }
        var selectedCredentialId by remember { mutableStateOf(matchedCredentials.first().id) }
        val coroutineScope = rememberCoroutineScope()
     
        Column(Modifier.verticalScroll(rememberScrollState())) {
            Text("REQUESTED DATA", style = MaterialTheme.typography.titleLarge)
            Card(Modifier.padding(vertical = 8.dp)) {
                Document(requested.docType, requested.namespaces.value.toUi())
            }
            Spacer(Modifier.padding(12.dp))
     
            Text("MATCHED CREDENTIALS", style = MaterialTheme.typography.titleLarge)
            Spacer(Modifier.padding(6.dp))
            matchedCredentials.forEach { matchedCredential ->
                // Step 3.2: Display matching credentials and claims
            }
     
            // Step 4.1: Send response
        }
    }
     
    // Step 3.1: Create function to add values to claims
     
    private fun Map<NameSpace, DataElements>.toUi() = mapValues { (_, dataElements) ->
        dataElements.value.keys.toSet()
    }

This code is very similar to the one used in the in the PresentationSelectCredentials.kt file in the Proximity Presentation tutorial, to avoid creating dependencies between the tutorials. In your own project you can use the same components for both presentation workflows.

  1. Add the following code under the // Step 2.1: Create an online presentation session comment to create a new online presentation session when the OnlinePresentationScreen composable appears on the screen:

    OnlinePresentationScreen.kt
    LaunchedEffect(requestUri) {
        withContext(Dispatchers.IO) {
            val mdocHolder = MobileCredentialHolder.getInstance()
            while (!mdocHolder.initialised) {
                delay(100)
            }
     
            session = mdocHolder
                .createOnlinePresentationSession(requestUri, requireTrustedVerifier = false)
        }
    }

This calls the SDK’s createOnlinePresentationSession function, passing requestUri as an argument, which is the Authorization request URI retrieved from the deep link/QR code. The function returns an OnlinePresentationSession object which is stored in the declared session variable.

This object includes a matchedCredentials property, which details the requested information (MobileCredentialRequest) and any existing credentials that match it (MobileCredentialMetaData). We will use this information in the next steps to display this information to the user.

We must wait for the SDK instance to be initialized, because in this tutorial the SDK initialization is called in a coroutine in the MainActivity.onCreate method.

We chose to set requireTrustedVerifier parameter to false because we want the SDK to trust all verifiers by default. If you require to interact with a limited list of verifiers, you may want to manually add trusted verifier certificates and set the parameter to true. You can learn more about certificate management in our SDK docs.

  1. In your MainActivity.kt file, add the following code under the // Online Presentation - Step 2.2: Add "Online Presentation" screen call comment to connect the created OnlinePresentationScreen composable to the navigation graph:

    MainActivity.kt
    composable(
        "onlinePresentation",
        deepLinks = listOf(
            navDeepLink { uriPattern = "mdoc-openid4vp://{wildcard}" }
        )
    ) {
        @Suppress("DEPRECATION")
        val deepLink = it.arguments
            ?.getParcelable<Intent>(NavController.KEY_DEEP_LINK_INTENT)
            ?.data
            ?.toString() ?: ""
     
        OnlinePresentationScreen(this@MainActivity, deepLink)
    }

Previously we added an intent filter for deep links with a mdoc-openid4vp scheme, so that the app is started when the intent with the deep link is filtered. Now, when the app is opened via this deep link, it will also start the OnlinePresentationScreen composable, and pass the deep link as the requestUri argument.

Handle a presentation request

We will now build the capability to use information from the OnlinePresentationSession object to handle the presentation request. This includes:

  • Displaying what information is requested.
  • Displaying what existing credentials match the requested information.
  • Getting user’s consent to share requested information from matching credentials.
  1. In the OnlinePresentationScreen.kt file, add the following code under the // Step 3.1: Create function to add values to claims comment to create a new function that will display the values of the claims the user is about to share:

    OnlinePresentationScreen.kt
    private fun List<MobileCredentialMetaData>.withClaimValues(
        from: MobileCredential
    ): List<MobileCredentialMetaData> = map { credential ->
        if (credential.id != from.id) {
            credential
        } else {
            credential.copy(
                claims = credential.claims.mapValues { (namespace, claims) ->
                    claims.map { claim ->
                        val claimValue = from.claims[namespace]?.get(claim)
                        claimValue?.let { "$claim: ${it.toUiString()}" } ?: claim
                    }.toSet()
                }
            )
        }
    }

    This function retrieves all matching credentials from the MobileCredentialMetaData object and retrieves their matching values from the internal storage according to the credential’s id.

  2. Add the following code under the // Step 3.2: Display matching credentials and claims comment to display to the user what credentials and claims they are about to share with the verifier, as well as a button that enables the user to display the value of these claims:

    OnlinePresentationScreen.kt
    val borderWidth = if (matchedCredential.id == selectedCredentialId) 4.dp else 0.dp
    Column(
        Modifier
            .clickable { selectedCredentialId = matchedCredential.id }
            .border(borderWidth, Color.Blue, RoundedCornerShape(16.dp))
            .padding(8.dp)
    ) {
        Card(Modifier.fillMaxWidth()) {
            Document(matchedCredential.docType, matchedCredential.claims)
        }
     
        Button(
            onClick = {
                val credentialWithValues = MobileCredentialHolder.getInstance()
                    .getCredential(matchedCredential.id, skipStatusCheck = true)
     
                matchedCredentials =
                    matchedCredentials.withClaimValues(from = credentialWithValues)
            },
            Modifier.fillMaxWidth()
        ) { Text("Show Values") }
    }
    Spacer(Modifier.padding(12.dp))
  3. Run the app and then close it (this updates the app on your testing device) and perform the following instructions:

    • Use a desktop browser to navigate to the MATTR Labs Maggie’s Groceries demo, where you must provide proof for purchasing an age restricted item.
    • In the checkout area, use the dropdown list to select Generic Wallet.
    • Select Share from wallet.
    • Open the camera on your testing mobile device and scan the QR code.
    • Confirm opening the QR code with your tutorial application.
    • The tutorial application should be launched on your testing mobile device, displaying the verification request and any matching credentials.

The result will look something like this:

Send response

After displaying matching credentials and claims to the user and enabling them to select what information to share, the last thing we need to do is build the capability to share the selected information with the verifier.

  1. Add the following code under the // Step 4.1: Send response comment to add a Send Response button, that will call the SDK’s sendResponse function and send the selected credential to the Verifier, when pressed:

    OnlinePresentationScreen.kt
    Button(
        onClick = {
            coroutineScope.launch {
                session?.sendResponse(listOf(selectedCredentialId), activity)
            }
        },
        Modifier.fillMaxWidth()
    ) { Text("Share information") }

Test the application

Let’s test that the application is working as expected in both workflows.

Same-device workflow

  1. Run the app and then close it (this updates the app on your testing device).
  2. Use a browser on your testing mobile device to navigate to the MATTR Labs Maggie’s Groceries demo, where you must provide proof for purchasing an age restricted item.
  3. In the checkout area, use the dropdown list to select Generic Wallet.
  4. Select Share from wallet.
  5. The tutorial application should be launched on your testing mobile device.
  6. Select the credential you wish to send to the verifier from the list of matched credentials.
  7. Select Send Response.
  8. You should be redirected back to Maggie’s online store and see a Over 18 years old verified indication.
  9. You can now proceed with the interaction (don’t expect any items to be shipped to you by Maggie’s though).

The result will look something like this:

Cross-device workflow

  1. Run the app and then close it (this updates the app on your testing device).
  2. Use a desktop browser to navigate to the MATTR Labs Maggie’s Groceries demo, where you must provide proof for purchasing an age restricted item.
  3. In the checkout area, use the dropdown list to select Generic Wallet.
  4. Select Share from wallet.
  5. Open the camera on your testing mobile device and scan the QR code.
  6. Confirm opening the QR code with your tutorial application.
  7. The tutorial application should be launched on your testing mobile device.
  8. Select the credential you wish to send to the verifier from the list of matched credentials.
  9. Select Send Response.
  10. Back on your desktop browser, you should see a Over 18 years old verified indication.
  11. You can proceed with the interaction (don’t expect any items to be shipped to you by Maggie’s though).

The result will look something like this:

Summary

You have just used the Android native mDoc Holder SDK to build an Android application that can present a claimed mDoc to a verifier remotely via an online presentation workflow as per ISO/IEC 18013-7:2024 and OID4VP.

Tutorial Workflow

This was achieved by building the following capabilities into the application:

  1. Handle an OID4VP request URI.
  2. Create an online presentation session.
  3. Handle a presentation request.
  4. Send a presentation response.

What’s next?

  • You can check out the Android mDoc holder SDK Docs to learn more about available functions and classes.