SDK Tethering
Learn how MATTR Holder SDKs are tethered to a MATTR VII tenant through Holder Application configuration, enabling capabilities like Wallet Attestation.
SDK Tethering ties each SDK/app instance to a MATTR VII tenant. Tethering establishes a trust relationship between your mobile application and the MATTR VII tenant, enabling the following capabilities:
- Operational insights: View details about registered and active app instances directly from your tenant.
- Wallet Attestation: Enable issuers to verify wallet integrity before issuing credentials. See Wallet Attestation for more information.
- Remote management channel: SDK Tethering establishes a channel that we expect to extend in the future with capabilities such as remote syncing of trusted issuer lists and eventing.
SDK Tethering is available from the following SDK versions:
- iOS Holder SDK: 6.0.0
- Android Holder SDK: 7.0.0
- React Native Holder SDK: 10.0.0
SDK Tethering is currently optional. You enable it by providing a platform configuration when initializing the SDK. If you initialize the SDK without a platform configuration, the SDK skips registration and tethering, and capabilities such as Wallet Attestation are unavailable. We expect to make SDK Tethering required in an upcoming release, so we recommend configuring it now to prepare.
How it works
The tethering process involves three steps:
- Configure a Holder Application on your MATTR VII tenant: You register your mobile app by creating a Holder Application, identified by the bundle identifier (iOS) or the package fingerprint (Android).
- Initialize the SDK with your tenant details: When you initialize the SDK in your app, you pass the details of the MATTR VII tenant and the Holder Application you configured on it.
- Automatic communication: Once initialized, instances of your app will automatically communicate with the configured MATTR VII tenant and retrieve the required tokens to operate and make requests to the tenant when required.
Token validity and offline use
The tokens issued during this process have configurable validity periods controlled by the
maxTimeOfflineInSecs field on your Holder Application configuration. This means your app can
function without internet connectivity to meet different use cases:
- Minimum: 1 day (86400 seconds)
- Maximum: 30 days (2592000 seconds)
- Default: 7 days (604800 seconds)
When the license token expires, the SDK must reconnect to the MATTR VII tenant to renew it.
Configuring SDK Tethering
Configure Holder Applications
SDK Tethering is optional. To enable it, create a Holder Application on your MATTR VII tenant for each platform target (iOS and Android). This is a one-time setup process that registers your app with the tenant and allows app instances to obtain the necessary tokens for authentication and operation.
Make a request of the following structure to create an iOS Holder Application configuration on your MATTR VII tenant:
POST /v1/holder/applications{
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}name: A unique name to identify this Holder Application.clientId: OAuth 2.0client_idvalue that the holder application uses when requesting client attestations. This value is included as thesubclaim in attestation JWTs and must match theclient_idconfigured by issuers who trust this Holder Application.type: Must beios.bundleId: The Bundle ID of your iOS app (must match your Xcode project configuration).teamId: Your Apple Developer Team ID.maxTimeOfflineInSecs: Maximum number of seconds the SDK can operate offline before requiring a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days (604800).appAttest: App Attest configuration for the iOS holder application:required: Whentrue, the app instance must provide a valid App Attest attestation during registration and token renewal. Whenfalse, the app can fall back to assertion-only authentication. See Attestation vs Assertion for more details.environment: The App Attest environment (developmentorproduction). Apple recommends usingdevelopmentfor testing andproductionfor distribution builds.
A successful response returns a 201 status code with the created Holder Application:
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"name": "My iOS Holder Application",
"clientId": "my-wallet-client",
"type": "ios",
"bundleId": "com.yourcompany.holderapp",
"teamId": "YOUR_APPLE_TEAM_ID",
"maxTimeOfflineInSecs": 864000,
"appAttest": {
"required": true,
"environment": "production"
}
}id: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly identify and authenticate your application.
Make a request of the following structure to create an Android Holder Application configuration on your MATTR VII tenant:
POST /v1/holder/applications{
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}name: A unique name to identify this Holder Application.clientId: OAuth 2.0client_idvalue that the holder application uses when requesting client attestations. This value is included as thesubclaim in attestation JWTs and must match theclient_idconfigured by Issuers who trust this Holder Application.type: Must beandroid.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.maxTimeOfflineInSecs: Maximum number of seconds the SDK can operate offline before requiring a new license token. Must be between 1 day (86400) and 30 days (2592000). Defaults to 7 days (604800).keyAttestation: Key Attestation configuration for the Android holder application:required: Whentrue, the app instance must provide a valid Key Attestation during registration and token renewal. Whenfalse, the app can register and renew tokens using just an authentication assertion. See Attestation vs Assertion for more details.
A successful response returns a 201 status code with the created Holder Application:
{
"id": "a82bfa46-72a0-4cde-b6cb-2a0de7e2f3c4",
"name": "My Android Holder Application",
"clientId": "my-wallet-client",
"type": "android",
"packageName": "com.yourcompany.holderapp",
"packageSigningCertificateThumbprints": [
"1232584B6F6A892D356899FB9576C5F226A179E6199F2B7A1D837B5C234C5A8E"
],
"maxTimeOfflineInSecs": 864000,
"keyAttestation": {
"required": true
}
}id: A unique identifier for the Holder Application (generated by the tenant). You must use this value when initializing the SDK so that it can correctly identify and authenticate your application.
For React Native applications, you must create both an iOS and an Android Holder Application on your MATTR VII tenant, and then conditionally pass the correct configuration based on the platform OS at runtime.
Step 1: Create the iOS Holder Application
Follow the instructions in the iOS tab to create a Holder Application configuration for iOS.
Step 2: Create the Android Holder Application
Follow the instructions in the Android tab to create a Holder Application configuration for Android.
Step 3: Pass the correct configuration based on Platform OS
When initializing the SDK, use Platform.OS to conditionally provide the matching Holder
Application configuration:
import { Platform } from "react-native";
const holderApplicationId =
Platform.OS === "ios"
? "YOUR_IOS_HOLDER_APPLICATION_ID"
: "YOUR_ANDROID_HOLDER_APPLICATION_ID";YOUR_IOS_HOLDER_APPLICATION_ID: Theidreturned when you created the iOS Holder Application.YOUR_ANDROID_HOLDER_APPLICATION_ID: Theidreturned when you created the Android Holder Application.
Initialize the SDK with platform configuration
If you are using SDK Tethering, update your SDK initialization to include the platform
configuration once your Holder Applications are created. This enables your app to connect to the
correct MATTR VII tenant and Holder Application. If you are not using tethering, you can initialize
the SDK without a platformConfiguration.
Initialize the SDK with your platform configuration:
let platformConfig = PlatformConfiguration(
tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
try await MobileCredentialHolder.shared.initialize(
platformConfiguration: platformConfig
)tenantHost: The URL of your MATTR VII tenant. This must be the tenant where your iOS Holder Application is configured.applicationId: Theidof your configured iOS Holder Application.
Initialize the SDK with your platform configuration:
val platformConfig = PlatformConfiguration(
tenantHost = URL("https://your-tenant.vii.mattr.global"),
applicationId = "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
)
MobileCredentialHolder.initialize(context, platformConfiguration = platformConfig)tenantHost: The URL of your MATTR VII tenant where your Android Holder Application is configured.applicationId: Theidof your configured Android Holder Application.
Since React Native bridges both iOS and Android, and each platform has its own Holder Application
registered on your MATTR VII tenant (see
Create a Holder Application), your
initialization code must pass the correct platform-specific applicationId at runtime. Use
Platform.OS to select the appropriate value:
import { initialize } from "@mattrglobal/mobile-credential-holder-react-native";
import { Platform } from "react-native";
const applicationId =
Platform.OS === "android"
? "your-android-holder-application-id"
: "your-ios-holder-application-id";
await initialize({
platformConfiguration: {
tenantHost: "https://your-tenant.vii.mattr.global",
applicationId,
},
});Replace the placeholder values with the id returned when you created each Holder Application. In
practice, you would typically store these values in a configuration file or environment variables.
Once your Holder Application configurations are created, your application will be able to use the SDK and interact with the MATTR VII platform (for example, to obtain attestation tokens).
Managing application instances
Once your Holder Application is configured and the SDK is initialized, each device that launches your app registers as a new application instance on your tethered MATTR VII tenant. You can view and manage these instances via the MATTR VII API.
Retrieve all registered instances
To view all registered instances for a Holder Application and track usage:
GET /v1/holder/applications/{applicationId}/instancesapplicationId: Theidof the Holder Application you want to inspect.
The response includes a paginated list of all registered instances:
{
"data": [
{
"id": "1ef1f867-20b4-48ea-aec1-bea7aff4964c",
"appAttestationType": "app_attestation",
"registeredAt": "2023-10-05T14:48:00.000Z",
"licenseExpiresAt": "2024-10-05T14:48:00.000Z",
"lastAttestedAt": "2023-12-01T10:30:00.000Z",
"externalReferenceId": "external-ref-12345",
"deviceDetails": {
"deviceModel": "iPhone 12",
"deviceMake": "Apple",
"osVersion": "iOS 14.4"
},
"sdkDetails": {
"sdkVersion": "1.2.3"
}
}
],
"nextCursor": "Y3JlYXRlZEF0PTIwMjAtMDgtMjVUMDY6NDY6MDkuNTEwWiZpZD1hNjZmZmVhNS04NDhlLTQzOWQtODBhNC1kZGE1NWY1M2UzNmM"
}Each instance includes:
id: Unique identifier for the registered instance.appAttestationType: The type of attestation used during registration (none,app_attestation, orkey_attestation).registeredAt: When the instance was first registered.licenseExpiresAt: When the instance's license expires (the Holder SDK will automatically handle license renewal).lastAttestedAt: When the instance was last attested.deviceDetails: Information about the device (model, make, OS version).sdkDetails: Information about the SDK version used by the instance.
This is useful for tracking how many devices are actively using your application and monitoring usage quotas.
Delete a specific instance
To remove a specific registered instance:
DELETE /v1/holder/applications/{applicationId}/instances/{instanceId}applicationId: Theidof the Holder Application.instanceId: Theidof the specific instance to delete.
Once deleted, the instance can no longer interact with the platform or receive tokens, and any existing tokens are revoked.
Deleting instances is primarily useful during testing when you have a limited number of devices and need to re-register a fresh instance (for example, to test the initial registration flow again). In production, there is nothing preventing the application from requesting another token on the next launch, which would create a new instance — so deleting instances is not an effective way to block a device.
Attestation vs Assertion fall-back
When configuring a Holder Application, you control whether your MATTR VII tenant requires attestation (hardware-backed proof of app integrity) or also accepts a lighter-weight assertion (a cryptographic signature proving key possession) during instance registration and token renewal.
Each platform has an attestation configuration with a required boolean:
- When
requiredistrue, the app instance must provide a valid attestation during registration and token renewal. - When
requiredisfalse, your tenant also accepts an assertion when an attestation is not available.
The SDK handles this automatically. It always attempts to provide an attestation, and falls back to
an assertion if it cannot generate one (for example, when the platform attestation service is
temporarily unavailable). Your tenant then accepts or rejects the request based on the required
setting. Your application does not need to manage attestation or assertion details directly.
When to use each setting
| Scenario | Recommended setting |
|---|---|
| Production apps in distribution | required: true — Provides the strongest integrity guarantees by verifying the app and device through OS-level attestation. |
| Development and testing | required: false — Useful when running on simulators or devices where attestation services are unavailable. |
| Broad device compatibility | required: false — Some older devices may not support hardware attestation. The assertion fall-back ensures these devices can still register. |
Setting attestation to required: false reduces the security guarantees of the tethering
process. Only use this setting when you have a specific need, such as supporting older devices
or during development.
Certificate expiry and required Key Attestation (Android)
This behavior is specific to Android Key Attestation. It does not apply to iOS App Attest.
When Key Attestation is set to required, an attestation must be present, parseable, and trusted (a trusted root with each certificate in the chain signed by the one above it) for registration to succeed.
One thing to be aware of: our validation does not check whether the certificates in the attestation chain have expired. Some Android devices, including relatively recent models, generate attestations where part or all of the certificate chain is already past its validity period. To avoid blocking these devices, an otherwise valid attestation with expired certificates is still treated as a successful attestation, even when Key Attestation is set to required.
In practice this means a device presenting an expired (but otherwise valid) attestation chain will register successfully and be marked as attested. This behavior may be tightened in a future release, so we recommend not relying on certificate expiry as part of your own trust decision.
How would you rate this page?
Last updated on