light-mode-image
Learn

Holder SDK quickstart guide

This quickstart is for evaluating the MATTR Pi mDocs Holder SDKs. In about 15-20 minutes you will configure a sample holder mobile app (iOS, Android, or React Native), run it on a physical device and use it to:

  1. Claim a credential issued through an OID4VCI workflow.
  2. Present that credential remotely to a web verifier application.
  3. Present that credential in-person to a verifier application on a different mobile device.

Estimated time: 15-20 minutes.

Use this guide as a fast evaluation path to see the Holder SDK working end-to-end on a real device. For detailed information and API examples, explore the individual tutorials and reference documentation for credential claiming, remote presentation, and proximity presentation.

Prerequisites

  • Access to the MATTR Pi mDocs Holder SDK for your chosen platform (iOS, Android, or React Native). Apply for access here.
  • A physical mobile device to run the holder application:
    • iOS/Android mobile device set up with biometric authentication and Bluetooth access.
  • Development environment set up for your chosen platform (e.g., Xcode for iOS, Android Studio for Android, or a React Native development environment).
  • Install the MATTR GO Verify example app on a separate mobile device for iOS or Android. This is used for in-person verification testing.

Evaluation Steps

In this quickstart you will:

  1. Configure and run a local sample holder project for your platform.
  2. Claim a credential into the sample holder app.
  3. Present that credential remotely to a web verifier.
  4. Present that credential in-person to a verifier app on a different device.

Use the tabs below to follow platform-specific setup steps.

Part 1: Configure the sample holder project (5-10 minutes)

  1. Download and unzip the sample iOS holder project.
  2. Use Xcode to open the .xcodeproj file in the unzipped folder.
  3. Drag the MobileCredentialHolderSDK-*version*.xcframework folder (obtained from MATTR as part of the SDK package) into your project.
  4. Configure MobileCredentialHolderSDK-*version*.xcframework to Embed & sign.
  5. Set a unique bundle identifier for the project in the Xcode project settings (e.g., com.yourname.holdersample).
  6. Run the project on a physical iOS device (simulators do not support the required Bluetooth capabilities).
  1. Download and unzip the sample Android holder project.
  2. Open the project in Android Studio.
  3. Unzip the mobile-credential-holder-*version*.zip file (obtained from MATTR as part of the SDK package).
  4. Drag the unzipped global folder into the project's repo folder.
  5. Sync Project with Gradle files to recognize the new module.
  6. Run the project on a physical Android device (emulators do not support the required Bluetooth capabilities).
  1. Access the sample holder codebase by either:

  2. Open the completed sample holder project in your code editor. You can find it in the sample-apps/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete directory.

  3. Open the app.config.ts file.

  4. Update the iOS bundleIdentifier to a unique value for your application, e.g. com.mycompany.myapp.

  5. Update the Android package to a unique value, e.g. com.mycompany.myapp.

  6. Navigate to the project directory and install dependencies:

    Install dependencies
    yarn install

You must be logged in to npm with access to the MATTR Pi mDocs Holder SDK package to install dependencies successfully. If you have not yet been granted access to the SDK, apply for access here.

  1. Run the following command to generate the iOS and Android project files:

    Generate native project files
    npx expo prebuild

    You should now see the ios and android folders in your project root.

  2. Connect your testing device and run the following command to start the application:

    iOS

    Run iOS application
    yarn ios --device

    Android

    Run Android application
    yarn android --device

Part 2: Claim a credential (3 minutes)

Use the sample holder app you just built to claim a credential from a MATTR VII tenant. This credential will be used in the subsequent presentation steps.

  1. Open the MATTR Labs Pre-authorized Offer tool.
  2. Select the Generate Credential Offer button.
    A QR code will be generated and rendered on the screen, along with a transaction code.
  3. Run your application.
  4. Select the Claim Credential button.
  5. Scan the QR code.
  6. Follow any on-screen instructions to claim the credential.

Under the hood, the sample app uses the Holder SDK to discover the credential offer from the QR code, and then claim and store the credential on the device using the OID4VCI Pre-authorized Code flow.

Part 3: Present the credential remotely (5 minutes)

Present the credential you just claimed to a web verifier application using the remote presentation workflow defined by ISO/IEC 18013-7:2025 and OID4VP.

  1. Use a mobile browser on the device where the sample application is installed to navigate to the MATTR Labs remote presentation testing tool.
  2. Select the OID4VP (Redirect) option from the Select Experience list.
  3. Select Request credentials.
  4. Select Allow to open the sample application.
  5. Select the credential you claimed in the previous part.
  6. Select Send Response.
  7. You should be redirected back to the MATTR Labs remote presentation testing tool, where you will see a successful verification indication.
  1. Use a desktop browser to navigate to the MATTR Labs remote presentation testing tool.
  2. Select OID4VP (Redirect) from the Select Experience list.
  3. Select Request credentials.
  4. Open the camera on the device where the sample application is installed and scan the QR code.
  5. Confirm opening the QR code with your sample application (this message might vary depending on your device and platform).
  6. Select the credential you claimed in the previous part.
  7. Select Send Response.
  8. Back on your desktop browser, you should see a successful verification indication.

In these flows, the sample app uses the Holder SDK to respond to the OID4VP authorization request, as defined in the OID4VP specification and ISO/IEC 18013-7:2025.

Part 4: Present the credential in-person (3 minutes)

Present the credential you claimed to a verifier application on a different mobile device using the proximity presentation workflow as defined by ISO/IEC 18013-5:2021.

Once you have the sample holder app running on one device, and the MATTR GO Verify app installed on a separate device, follow these steps to test in-person presentation:

  1. On the sample holder app, select Present Credentials.
  2. The app will display a QR code containing the device engagement string.
  3. On the verifier device, launch the MATTR GO Verify app, select Scan and scan the QR code from the holder app.
  4. On the sample holder app, review the verifier's request and consent to share the credential.
  5. The verifier application will display the verification results.

Behind the scenes, the Holder SDK handled device engagement, established a secure BLE session, and presented the mDoc to the verifier as per ISO/IEC 18013-5:2021.

Review the codebase

The sample holder application uses the Holder SDK to claim and present mDocs. Once you have the sample application running, use this section to understand the key SDK calls you will reuse in your own application.

Initialize the SDK

Initialize the SDK
try mobileCredentialHolder.initialize(
    userAuthenticationConfiguration: UserAuthenticationConfiguration( 
        userAuthenticationBehavior: .always, 
        userAuthenticationType: .biometricOrPasscode, 
        presentationTimeoutSeconds: 20
    ),
    credentialIssuanceConfiguration: CredentialIssuanceConfiguration(
        redirectUri: "<WalletRedirectUri>", 
        autoTrustMobileCredentialIaca: true
    ),
    dcConfiguration: DCConfiguration( // iOS 26+ only
        appGroup: "group.com.yourcompany.app", 
        supportedDocTypes: [.mDL, .eudi] 
    ) 
)
  • userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. The example shows the default values. Refer to the reference documentation for additional details about the available configuration options.
  • redirectUri : Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for the Authorization Code flow.
  • autoTrustMobileCredentialIaca : Set to true if you trust the credential issuer(s) for any offer. Defaults to false.
  • dcConfiguration: Optional, iOS 26+ only. Configures Digital Credentials API support for presenting credentials via an OS-native overlay. Specify your app group and supported document types (e.g., .mDL, .eudi, .euav, .photoid, .jpMnc).
Initialize the SDK
mobileCredentialHolder.initialize(
    activity,
    userAuthenticationConfiguration = UserAuthenticationConfiguration( 
        behavior = UserAuthenticationBehavior.Always, 
        presentationTimeoutSeconds = 20
    ),
    credentialIssuanceConfiguration = CredentialIssuanceConfiguration(
        redirectUri = "<WalletRedirectUri>", 
        autoTrustMobileCredentialIaca = true
    )
)
  • userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. The example shows the default values. Refer to the reference documentation for additional details about the available configuration options.
  • redirectUri : Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for the Authorization Code flow.
  • autoTrustMobileCredentialIaca : Set to true if you trust the credential issuer(s) for any offer. Defaults to false.
Initialize the SDK
const initializeResult = await mobileCredentialHolder.initialize({
  userAuthenticationConfiguration: {
    userAuthenticationBehavior: UserAuthenticationBehavior.OnInitialize, 
    userAuthenticationType: UserAuthenticationType.BiometricOrPasscode, 
  },
  credentialIssuanceConfiguration: {
    autoTrustMobileCredentialIaca: true, 
    redirectUri: "<WalletRedirectUri>", 
  },
});
if (initializeResult.isErr()) {
  const { error } = initializeResult;
  // handle error scenarios
  return;
}
  • userAuthenticationConfiguration: Optional. Configures when the SDK requires user authentication to access or present credentials. Refer to the reference documentation for additional details about the available configuration options.
  • redirectUri: Provide a URL that corresponds to a specific path in your application. The SDK will redirect the user to this path after they successfully complete authentication. Required for the Authorization Code flow.
  • autoTrustMobileCredentialIaca: Set to true if you trust the credential issuer(s) for any offer. Defaults to false.

Credential claiming workflow

Discover credential offer

Discover credential offer
    let discoveredOffer = try await mobileCredentialHolder.discoverCredentialOffer(offerUrl: String)
  • offerUrl : Pass the OID4VCI initiation URL.

The discoverCredentialOffer method returns a DiscoveredCredentialOffer object containing the offer details.

Discover credential offer
val discoveredOffer = mobileCredentialHolder.discoverCredentialOffer(offerUri)
  • offerUrl : Pass the OID4VCI initiation URL as a string.

The discoverCredentialOffer method returns an instance of DiscoveredCredentialOffer containing the offer details.

Discover credential offer
const discoveredOfferResult =
  await mobileCredentialHolder.discoverCredentialOffer(offerUrl); 
if (discoveredOfferResult.isErr()) {
  const { error } = discoveredOfferResult;
  // handle error scenarios
  return;
}
const credentialOffer = discoveredOfferResult.value;
  • offerUrl : Pass the OID4VCI initiation URL.

The discoverCredentialOffer method returns a Result<CredentialOfferResponse, Error> (using the neverthrow pattern) object containing the offer details and/or any errors.

Claim and store credentials

Claim credentials
let retrievedCredentialResults = try await mobileCredentialHolder.retrieveCredentials(
    credentialOffer: String, 
    clientId: "<WalletClientID>", 
    transactionCode: nil
)
  • credentialOffer : Pass the OID4VCI credential offer URI.
  • clientId : Pass a string that identifies your wallet application with the issuer.
  • transactionCode : Provide a transaction code if the Pre-Authorization Code flow offer requires a transaction code.

The retrieveCredentials method returns an array of RetrieveCredentialResult objects, each containing the metadata of the retrieved credentials, including the credential ID (credentialId).

Claim credentials
val credentialResults = mobileCredentialHolder.retrieveCredentials(
    activity = activity,
    credentialOffer = offerUri, 
    clientId = "<WalletClientID>", 
    transactionCode = null
)
  • credentialOffer : Pass the OID4VCI credential offer URI.
  • clientId : Pass a string that identifies your wallet application with the issuer.
  • transactionCode : Provide a transaction code if claiming a credential using a Pre-authorized Code flow that requires a transaction code.

The retrieveCredentials method returns an array of RetrieveCredentialResult objects, each containing the metadata of the retrieved credentials, including the credential ID (credentialId).

Claim credentials
const retrievedCredentialsResults =
  await mobileCredentialHolder.retrieveCredentials({
    credentialOffer: offerUrl, 
    clientId: "<WalletClientID>", 
    transactionCode: undefined, 
  });
if (retrievedCredentialsResults.isErr()) {
  const { error } = retrievedCredentialsResults;
  // handle error scenarios
  return;
}
const retrievedCredentials = retrievedCredentialsResults.value;

const retrievedCredential = retrievedCredentials[0];
if (retrievedCredential?.error || !retrievedCredential?.credentialId) {
  // handle error scenarios
  return;
}
const credentialId = retrievedCredential.credentialId;
  • credentialOffer: Pass the OID4VCI credential offer URI.
  • clientId: Pass a string that identifies your wallet application with the issuer.
  • transactionCode: Provide a transaction code if the Pre-Authorization Code flow offer requires a transaction code.

The retrieveCredentials method returns a Result<RetrieveCredentialsResponse, Error> (using the neverthrow pattern) object containing the metadata of the retrieved credentials, including the credential ID (credentialId).

Access claimed credentials

Access credential
let credential = try mobileCredentialHolder.getCredential(credentialId: String)
  • credentialId : Pass the credential ID of the credential you want to access.
Access credential
val credential = mobileCredentialHolder.getCredential(credentialId)
  • credentialId : Pass the credential ID of the credential you want to access, as a string.
Access credential
const credentialResult =
  await mobileCredentialHolder.getCredential(credentialId); 
if (credentialResult.isErr()) {
  const { error } = credentialResult;
  // handle error scenarios
  return;
}
const credential = credentialResult.value;
  • credentialId : Pass the credential ID of the credential you want to access.

Remote presentation handling

Register the verifier's Authorization URL

Ensure that your application is registered to handle the verifier's authorization URL. This is typically done in the app's Info.plist file. The URL scheme should match the one used by the verifier.

Ensure that your application is registered to handle the verifier's authorization URL. This is typically done in the AndroidManifest.xml file. The URL scheme should match the one used by the verifier.

Ensure that your application is registered to handle the verifier's authorization URL. The URL scheme should match the one used by the verifier.

  • For iOS this is typically done in the app's Info.plist file.
  • For Android this is typically done in the AndroidManifest.xml file.

Create an online presentation session

Create an online presentation session
    let onlinePresentationSession = try await mobileCredentialHolder.createOnlinePresentationSession(authorizationRequestUri: authorizationRequestURI, requireTrustedVerifier: false)
  • authorizationRequestURI : Pass the OID4VP authorization request URI as a string.
  • requireTrustedVerifier : Set to true if you want to ensure that the verifier exists on the app's trusted verifier's list.

The createOnlinePresentationSession method returns an instance of OnlinePresentationSession containing requested and matching credentials.

Create an online presentation session
    val onlinePresentationSession = mobileCredentialHolder.createOnlinePresentationSession(
        authorisationRequestUri = authorizationRequestUri,
        requireTrustedVerifier = false
    )
  • authorisationRequestURI : Pass the OID4VP authorization request URI as a string.
  • requireTrustedVerifier : Set to true if you want to ensure that the verifier exists on the app's trusted verifier's list.

The createOnlinePresentationSession method returns an instance of OnlinePresentationSession containing requested and matching credentials.

Create an online presentation session
const createSessionResult =
  await mobileCredentialHolder.createOnlinePresentationSession({
    authorizationRequestUri: authorizationRequestURI,
    requireTrustedVerifier: false,
  });
if (createSessionResult.isErr()) {
  const { error } = createSessionResult;
  // handle error scenarios
  return;
}
const onlinePresentationSession = createSessionResult.value;
  • authorizationRequestUri : Pass the OID4VP authorization request URI as a string.
  • requireTrustedVerifier : Set to true if you want to ensure that the verifier exists on the app's trusted verifier's list.

The createOnlinePresentationSession method returns an instance of OnlinePresentationSession containing requested and matching credentials.

Present matching credentials to holder

Present matching credentials to the holder
    let credential = try await mobileCredentialHolder.getCredential(credentialId: id)

Your application must then gather the user's consent to share the matching credential with the verifier.

Present matching credentials to the holder
    val credential = mobileCredentialHolder.getCredential(credentialId)

Your application must then gather the user's consent to share the matching credential with the verifier.

Present matching credentials to the holder
const credentialResult = await mobileCredentialHolder.getCredential(
  credentialId, 
);
if (credentialResult.isErr()) {
  const { error } = credentialResult;
  // handle error scenarios
  return;
}
const credential = credentialResult.value;
  • credentialId : Pass the identifier of the credential you want to present, as a string. This information can be retrieved from the OnlinePresentationSession object returned by the method.

Your application must then gather the user's consent to share the matching credential with the verifier.

Send a response to the verifier

Send response to the verifier
    try await onlinePresentationSession.sendResponse(credentialIds: [id])
  • id : Pass the identifier of the credential you want to send, as a string.
Send response to the verifier
onlinePresentationSession.sendResponse(
    credentialIds = listOf(credentialId), 
    activity = activity
)
  • credentialId : Pass the identifier of the credential you want to send, as a string.
Send response to the verifier
const sendResponseResult = await onlinePresentationSession.sendResponse({
  credentialIds: [credentialId],
});
if (sendResponseResult.isErr()) {
  const { error } = sendResponseResult;
  // handle error scenarios
  return;
}
  • credentialId : Pass the identifier of the credential you want to send, as a string.

Proximity presentation handling

Create a proximity presentation session

Create a proximity presentation session
    let proximityPresentationSession = try await mobileCredentialHolder.createProximityPresentationSession(
                    onRequestReceived: onRequestReceived(_:error:)
                    // 1. Callback triggered when the verifier requests a credential for verification. Use this callback to display the request on the UI and get the user's consent to share any matched credentials.
                )

The createProximityPresentationSession method returns an instance of ProximityPresentationSession which contains a deviceEngagement property that must be shared with the verifier embedded in a QR code to establish a secure connection.

Create a proximity presentation session
val proximityPresentationSession = mobileCredentialHolder.createProximityPresentationSession(
    onRequestReceived = { session, matchedCredentials, error ->
        if (error != null) {
             // Handle error
        } else {
            // 1. Show request on UI
            // 2. Show matched credentials on UI and get user's consent to share them
            // 3:
            session.sendResponse(
                val credentialIds = matchedCredentials!!.flatMap {
                    it.matchedCredentials.map { credential -> credential.id }
                } ,
                activity = activity
            )
        }
    }
)

The createProximityPresentationSession method returns an instance of ProximityPresentationSession which contains a deviceEngagement property that must be shared with the verifier embedded in a QR code to establish a secure connection.

Create a proximity presentation session
const createSessionResult =
  await mobileCredentialHolder.createProximityPresentationSession({
    onRequestReceived: (data) => {
      if ("error" in data) {
        // handle error scenarios
        return;
      }

      // 1. Show UI prompt and gather user consent
      // 2. Retrieve matching credential from request object
    },
  });
if (createSessionResult.isErr()) {
  const { error } = createSessionResult;
  // handle error scenarios
  return;
}
const proximityPresentationSession = createSessionResult.value;

The createProximityPresentationSession method returns an instance of ProximityPresentationSession which contains a deviceEngagement property that must be shared with the verifier embedded in a QR code to establish a secure connection.

Present matching credentials to holder

Present matching credentials to the holder
    let credential = try await mobileCredentialHolder.getCredential(credentialId: id)
  • id : Pass the identifier of the credential you want to present, as a string. This information is returned by the createProximityPresentationSession method on a onRequestReceived event, triggered when the verifier requests a credential for verification.

Your application must then gather the user's consent to share the matching credential with the verifier.

Present matching credentials to the holder
    val credential = mobileCredentialHolder.getCredential(credentialId)
  • credentialId : Pass the identifier of the credential you want to present, as a string. This information is returned by the createProximityPresentationSession method on a onRequestReceived event, triggered when the verifier requests a credential for verification.

Your application must then gather the user's consent to share the matching credential with the verifier.

Present matching credentials to the holder
const credentialResult =
  await mobileCredentialHolder.getCredential(credentialId); 
if (credentialResult.isErr()) {
  const { error } = credentialResult;
  // handle error scenarios
  return;
}
const credential = credentialResult.value;

//
// Send a response to the verifier
//
const sendResponseResult =
  await mobileCredentialHolder.sendProximityPresentationResponse({
    credentialIds: [credentialId],
    terminateSession: false,
  });
if (sendResponseResult.isErr()) {
  const { error } = sendResponseResult;
  // handle error scenarios
  return;
}
  • credentialId : Pass the identifier of the credential you want to present, as a string. This information is returned by the createProximityPresentationSession method on a onRequestReceived event, triggered when the verifier requests a credential for verification.

Your application must then gather the user's consent to share the matching credential with the verifier.

Send a response to the verifier

Send response to the verifier
    try await proximityPresentationSession.sendResponse(credentialIds: [id])
  • id : Pass the identifier of the credential you want to send, as a string.
Send response to the verifier
    proximityPresentationSession?.sendResponse(credentialIds = listOf(credentialId))
  • credentialId : Pass the identifier of the credential you want to send, as a string.
Send response to the verifier
const sendResponseResult =
  await mobileCredentialHolder.sendProximityPresentationResponse({
    credentialIds: [credentialId],
  });
if (sendResponseResult.isErr()) {
  const { error } = sendResponseResult;
  // handle error scenarios
  return;
}
  • credentialId : Pass the identifier of the credential you want to send, as a string.

Next steps

How would you rate this page?

Last updated on

On this page