light-mode-image
Learn
Management & OperationsMigration Guides

Android Holder SDK v7.0.0 Migration Guide

A comprehensive guide to migrating to Android Holder SDK v7.0.0, covering breaking changes, new features, and step-by-step migration instructions.

This is a draft migration guide, shared ahead of general availability to help you gauge migration effort and plan ahead of the release. We will update this page to a final copy when the SDK is generally available.

Overview

This guide provides a comprehensive overview of the changes introduced in the Android Holder SDK v7.0.0, including breaking changes, new features, and migration steps.

This release focuses on strengthening trust at issuance, improving predictability in credential handling, and increasing consistency across platforms. It introduces Wallet Attestation support, enabling issuers to verify wallet integrity before issuing credentials, along with stronger validation and resilience in credential flows.

Key Features

  • Wallet Attestation support: The Android Holder SDK now supports Wallet Attestation, allowing your holder application to prove to issuers that it is a trusted wallet before credentials are issued. The SDK is now tethered to a MATTR VII tenant, which issues wallet attestation tokens when required by an issuer.
  • Stronger application identity during issuance: Credential issuance flows now consistently use the application's client_id, ensuring the holder is accurately represented when interacting with issuers. This improves compatibility with issuers applying stricter controls.
  • More predictable credential retrieval results: Credential retrieval responses are now more structured and deterministic, explicitly identifying success or failure with guaranteed fields per result type.
  • Stronger validation in presentation flows: Stricter JWT request validation, state size validation, and certificate chain validation improve how the SDK handles malformed or incomplete requests.
  • Improved resilience in issuance flows: Oversized credential payload validation prevents failure modes that could lead to degraded user experiences.
  • General stability and performance improvements: Multiple refinements reduce integration friction, increase consistency, and improve overall reliability.

Breaking Changes

This section outlines the breaking changes introduced in v7.0.0 that require updates to your existing implementation:

#ChangeImpact
1SDK initialization now requires platform/tenant configurationYou must create a holder application on your MATTR VII tenant and update all initialization call sites to provide tenant configuration.
2Pre-authorized code flow now uses the application's client_id instead of a default identifierEnsure your application has a valid configured client_id and issuers recognize it.
3Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fieldsUpdate result parsing logic to use success/failure branching.
4doctype renamed to docType in credential retrievalRename all usages of doctype to docType.
5MobileCredentialAuthenticationOption renamed to DeviceAuthenticationOptionUpdate all imports and type references.
6Stricter JWT request validation for online presentations: enforces expected typ, validates iat, rejects expired expWhere possible, ensure verifiers generate compliant JWT authorization request objects.
7Oversized state values in VP online presentation requests are now rejectedWhere possible, ensure verifier-generated state values remain within supported limits.
8Oversized credential fields in pre-authorized issuance are now rejectedWhere possible, validate issuer payload sizes and test with realistic credential data.
9mdocIacasUri is now optionalReview assumptions that this field is always present.

Migration Steps

Create a holder application on your MATTR VII tenant

The Android Holder SDK is now tethered to a MATTR VII tenant. The SDK will not function without a holder application configuration on your tenant. This is required regardless of whether wallet attestation is used in your credential flows.

To register your Android application, make a request to create a holder application:

Request
POST /v1/holder/applications
Request body
{
    "name": "My Android Holder Application",
    "clientId": "your-wallet-client-id",
    "type": "android",
    "packageName": "com.yourcompany.holderapp",
    "packageSigningCertificateThumbprints": [
        "1232584b6f6a892d356899fb9576c5f226a179e6199f2b7a1d837b5c234c5a8e"
    ]
}
  • name: A unique name to identify your holder application.
  • clientId: The OAuth 2.0 client_id that identifies your wallet application. This value is included in attestation JWTs and must match the client_id configured on the issuer's Authorization Server.
  • type: Must be android for an Android application.
  • packageName: The package name of your Android application.
  • packageSigningCertificateThumbprints: SHA-256 hex-encoded fingerprints of the signing key certificates used to sign your APK or app bundle. This ensures the tenant only accepts requests from known and trusted applications. Refer to Android app signing for more information.

Once created, your holder application will be able to use the SDK and interact with the MATTR VII platform (for example, to obtain attestation tokens).

The response will include a unique id for your application, which must be used when initializing the SDK so that it can correctly identify and authenticate your application.

The clientId you configure here is the same value you must:

  1. Pass as the clientId parameter when calling retrieveCredentials.
  2. Register with each issuer you intend to interact with, so the issuer can identify and trust requests coming from your wallet application.

Update SDK initialization

SDK initialization now requires platform/tenant configuration. Update all initialization call sites to provide the required configuration:

- MobileCredentialHolder.initialize(context, instanceId)
+ val platformConfig = PlatformConfiguration(
+     tenantUrl = "https://your-tenant.vii.mattr.global",
+     appIdentifier = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ MobileCredentialHolder.initialize(context, instanceId, platformConfig)
  • tenantUrl: The URL of your MATTR VII tenant where your holder application is configured.
  • appIdentifier: The id of your configured MATTR VII holder application.

Handle new Wallet Attestation error modes

Wallet Attestation introduces new error scenarios that can occur during credential issuance. These errors may arise when:

  • The SDK requests an attestation token from your MATTR VII tenant.
  • The SDK presents wallet attestation credentials to an issuer's Authorization Server at its /token endpoint.
  • The SDK requests credentials from an issuer's /credential endpoint with DPoP-bound access tokens.

The following methods are affected:

  • retrieveCredentials — may throw new exception types and return new per-credential failure types.

New exceptions will be added to HolderException (a breaking change requiring updates to exhaustive when blocks), along with new RetrieveCredentialError types.

Update your error handling, logging, analytics, and support diagnostics to account for new wallet attestation error cases. An exhaustive list of new errors will be provided with the final migration guide.

Update client_id configuration

Previously, the client_id passed to retrieveCredentials was not shared with the issuer during the pre-authorized code flow, so any value would work. This is no longer the case — the SDK now presents the client_id to the issuer as part of wallet attestation, and the issuer validates it against its list of trusted wallet providers.

To prepare for this change:

  1. Coordinate with the issuer to register your wallet application as a trusted wallet provider. The issuer will provide you with a client_id that identifies your application.
  2. Pass the issuer-provided client_id when calling retrieveCredentials.
  val results = holder.retrieveCredentials(
      activity = activity,
      credentialOffer = offer,
-     clientId = "any-value",
+     clientId = "issuer-provided-client-id"
  )

Issuance flows that previously worked with an arbitrary client_id will fail if the issuer requires a trusted wallet provider. Ensure you have coordinated with each issuer and obtained the correct client_id before upgrading. Test direct issuance flows to confirm credentials are issued successfully.

Update credential retrieval result handling

RetrieveCredentialResult has been converted from a data class with nullable fields to a sealed interface with explicit Success and Failure subtypes. Each subtype carries guaranteed properties, removing ambiguity from result handling.

The retrieveCredentials function returns List<RetrieveCredentialResult> — a list with one result per offered credential. Update your iteration logic to use when matching:

  val results = holder.retrieveCredentials(options)
  for (result in results) {
-     val credentialId = result.credentialId
-     if (credentialId != null) {
-         // Use result.docType and credentialId
-     } else {
-         // Use result.docType and result.error
-     }
+     when (result) {
+         is RetrieveCredentialResult.Success -> {
+             // result.docType and result.credentialId are guaranteed
+         }
+         is RetrieveCredentialResult.Failure -> {
+             // result.docType and result.error are guaranteed
+         }
+     }
  }

Update tests and any downstream logic that checked for null fields.

Rename doctype to docType

The credential retrieval field has been renamed from doctype to docType. Update all usages across your codebase:

- val documentType = credential.doctype
+ val documentType = credential.docType

Update models, serializers, mocks, tests, and any cross-platform abstraction layers.

Rename MobileCredentialAuthenticationOption to DeviceAuthenticationOption

Update all imports, type references, and configuration objects:

- import global.mattr.mobilecredential.holder.MobileCredentialAuthenticationOption
+ import global.mattr.mobilecredential.holder.DeviceAuthenticationOption

- val authOption: MobileCredentialAuthenticationOption = ...
+ val authOption: DeviceAuthenticationOption = ...

Update verifier JWT request objects

The SDK now enforces stricter JWT request validation for online presentations:

  • The typ header must match the expected value.
  • The iat (issued at) claim is now validated.
  • Expired exp (expiration) values are now rejected.

Where possible, ensure all verifiers in your ecosystem generate compliant JWT authorization request objects. Update test fixtures and any non-compliant verifier integrations.

Validate state values in presentation requests

Oversized state values in VP online presentation requests are now rejected. Where possible, ensure verifier-generated state values remain within supported limits. Update tests that use large state payloads.

Ensure x5c certificate chains are present

Missing x5c certificate chains in validity-signed JWT request objects are now safely rejected. Where possible, ensure verifier request objects include required certificate chains where applicable. Update negative-path handling for rejected presentation requests.

Validate credential payload sizes

Oversized credential fields in pre-authorized issuance are now rejected. Where possible, validate issuer payload sizes and test issuance with realistic credential data. Update error handling for rejected oversized credentials:

  val result = holder.retrieveCredentials(options)
  when (result) {
      is RetrieveCredentialsResult.Failure -> {
          when (result.error) {
              // ... existing error cases
+             is OversizedCredentialPayload -> {
+                 // Handle oversized credential rejection
+             }
          }
      }
  }

Handle optional mdocIacasUri

The mdocIacasUri field is now optional. Review any app-side assumptions that this field is always present and confirm handling of empty string values:

- val iacasUri = credential.mdocIacasUri // assumed non-null
+ val iacasUri = credential.mdocIacasUri // may be empty string
+ if (iacasUri.isNotEmpty()) {
+     // Use the URI
+ }

How would you rate this page?

Last updated on

On this page