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:
| # | Change | Impact |
|---|---|---|
| 1 | SDK initialization now requires platform/tenant configuration | You must create a holder application on your MATTR VII tenant and update all initialization call sites to provide tenant configuration. |
| 2 | Pre-authorized code flow now uses the application's client_id instead of a default identifier | Ensure your application has a valid configured client_id and issuers recognize it. |
| 3 | Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fields | Update result parsing logic to use success/failure branching. |
| 4 | doctype renamed to docType in credential retrieval | Rename all usages of doctype to docType. |
| 5 | MobileCredentialAuthenticationOption renamed to DeviceAuthenticationOption | Update all imports and type references. |
| 6 | Stricter JWT request validation for online presentations: enforces expected typ, validates iat, rejects expired exp | Where possible, ensure verifiers generate compliant JWT authorization request objects. |
| 7 | Oversized state values in VP online presentation requests are now rejected | Where possible, ensure verifier-generated state values remain within supported limits. |
| 8 | Oversized credential fields in pre-authorized issuance are now rejected | Where possible, validate issuer payload sizes and test with realistic credential data. |
| 9 | mdocIacasUri is now optional | Review 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:
POST /v1/holder/applications{
"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.0client_idthat identifies your wallet application. This value is included in attestation JWTs and must match theclient_idconfigured on the issuer's Authorization Server.type: Must beandroidfor 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:
- Pass as the
clientIdparameter when callingretrieveCredentials. - 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: Theidof 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
/tokenendpoint. - The SDK requests credentials from an issuer's
/credentialendpoint 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:
- Coordinate with the issuer to register your wallet application as a trusted wallet provider. The issuer will provide you with a
client_idthat identifies your application. - Pass the issuer-provided
client_idwhen callingretrieveCredentials.
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.docTypeUpdate 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
typheader 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