diff --git a/android-in-person-verifier-tutorial-sample-app/app/build.gradle.kts b/android-in-person-verifier-tutorial-sample-app/app/build.gradle.kts index 4cd523e..cb1e13f 100644 --- a/android-in-person-verifier-tutorial-sample-app/app/build.gradle.kts +++ b/android-in-person-verifier-tutorial-sample-app/app/build.gradle.kts @@ -56,7 +56,7 @@ dependencies { androidTestImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.test.manifest) - implementation("global.mattr.mobilecredential:verifier:6.1.0") + implementation("global.mattr.mobilecredential:verifier:7.0.0") implementation("androidx.navigation:navigation-compose:2.9.0") implementation("com.google.accompanist:accompanist-permissions:0.36.0") implementation("com.journeyapps:zxing-android-embedded:4.3.0") diff --git a/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/MainActivity.kt b/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/MainActivity.kt index 079c102..f100586 100644 --- a/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/MainActivity.kt +++ b/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/MainActivity.kt @@ -21,7 +21,11 @@ import androidx.navigation.compose.rememberNavController import com.example.verifiertutorial.ui.theme.VerifierTutorialTheme import global.mattr.mobilecredential.verifier.dto.MobileCredentialResponse import global.mattr.mobilecredential.verifier.MobileCredentialVerifier +import global.mattr.mobilecredential.common.platformconfig.PlatformConfiguration +import global.mattr.mobilecredential.verifier.exception.VerifierException.FailedToRegisterException +import global.mattr.mobilecredential.verifier.exception.VerifierException.InvalidLicenseException import kotlinx.coroutines.launch +import java.net.URL class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -30,12 +34,25 @@ class MainActivity : ComponentActivity() { // The SDK must be initialized before any other SDK functions are called. // Initialize SDK - Step 1.1: Initialize the SDK lifecycleScope.launch { - // This function initializes storage and performs validations to ensure the SDK is ready for use. - // Only the application context is persisted. - MobileCredentialVerifier.initialize(this@MainActivity) - // Setup certificates - Step 2.1: Add trusted issuer certificates - // The added certificates will be the trust chain roots used to verify signatures of the received credentials. - MobileCredentialVerifier.addTrustedIssuerCertificates(listOf(Iacas.mattrLabs)) + // From Android Verifier SDK v7.0.0, the SDK is tethered to a MATTR VII tenant. Initialization + // registers this app instance with the tenant and obtains a license, so a PlatformConfiguration + // pointing at your tenant and Verifier Application is required. + val platformConfiguration = PlatformConfiguration( + tenantHost = URL("https://your-tenant.vii.mattr.global"), + applicationId = "" + ) + try { + // This function initializes storage, registers the app instance, obtains a license, + // and performs validations to ensure the SDK is ready for use. + MobileCredentialVerifier.initialize(this@MainActivity, platformConfiguration) + // Setup certificates - Step 2.1: Add trusted issuer certificates + // The added certificates will be the trust chain roots used to verify signatures of the received credentials. + MobileCredentialVerifier.addTrustedIssuerCertificates(listOf(Iacas.mattrLabs)) + } catch (e: FailedToRegisterException) { + // Registration with the MATTR VII tenant failed — check connectivity and configuration. + } catch (e: InvalidLicenseException) { + // The SDK license is missing, invalid, or expired. + } } enableEdgeToEdge() diff --git a/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/ViewResponseScreen.kt b/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/ViewResponseScreen.kt index 8e15c75..9d390d9 100644 --- a/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/ViewResponseScreen.kt +++ b/android-in-person-verifier-tutorial-sample-app/app/src/main/java/com/example/verifiertutorial/ViewResponseScreen.kt @@ -32,8 +32,10 @@ import global.mattr.mobilecredential.verifier.dto.MobileCredentialElement fun ViewResponseScreen() { // Verify mDocs - Step 4.5: Define content // For tutorial simplicity, only the first credential in the response is displayed, since the sample request only asks for a single document type (mDL). + // From Android Verifier SDK v7.0.0, MobileCredentialResponse.credentials and .credentialErrors + // are non-nullable lists, so we check for emptiness rather than null. val credential = SharedData.credentialResponse?.credentials?.firstOrNull() - if (credential == null || SharedData.credentialResponse?.credentialErrors != null) { + if (credential == null || SharedData.credentialResponse?.credentialErrors?.isNotEmpty() == true) { // Verify mDocs - Step 4.6: Show error // Show an error if something went wrong during the retrieval Box(Modifier.fillMaxSize()) { diff --git a/android-remote-verification-tutorial-sample-app/app/build.gradle.kts b/android-remote-verification-tutorial-sample-app/app/build.gradle.kts index e5bd1d5..e7471f2 100644 --- a/android-remote-verification-tutorial-sample-app/app/build.gradle.kts +++ b/android-remote-verification-tutorial-sample-app/app/build.gradle.kts @@ -62,6 +62,6 @@ dependencies { debugImplementation(libs.androidx.compose.ui.test.manifest) // MATTR MobileCredentialVerifier SDK — resolved from the local `repo` directory. - implementation("global.mattr.mobilecredential:verifier:6.1.0") + implementation("global.mattr.mobilecredential:verifier:7.0.0") implementation("androidx.navigation:navigation-compose:2.9.0") } diff --git a/android-remote-verification-tutorial-sample-app/app/src/main/java/com/example/mobileverifiertutorial/MainActivity.kt b/android-remote-verification-tutorial-sample-app/app/src/main/java/com/example/mobileverifiertutorial/MainActivity.kt index a176b2f..07fa7fc 100644 --- a/android-remote-verification-tutorial-sample-app/app/src/main/java/com/example/mobileverifiertutorial/MainActivity.kt +++ b/android-remote-verification-tutorial-sample-app/app/src/main/java/com/example/mobileverifiertutorial/MainActivity.kt @@ -28,11 +28,14 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel import com.example.mobileverifiertutorial.ui.theme.MobileVerifierTutorialTheme import global.mattr.mobilecredential.verifier.MobileCredentialVerifier -import global.mattr.mobilecredential.verifier.PlatformConfiguration +import global.mattr.mobilecredential.verifier.OnlinePresentationSessionResult +import global.mattr.mobilecredential.common.platformconfig.PlatformConfiguration import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.DataElements import global.mattr.mobilecredential.verifier.deviceretrieval.devicerequest.NameSpaces import global.mattr.mobilecredential.verifier.dto.MobileCredentialPresentation import global.mattr.mobilecredential.verifier.dto.MobileCredentialRequest +import global.mattr.mobilecredential.verifier.exception.VerifierException.FailedToRegisterException +import global.mattr.mobilecredential.verifier.exception.VerifierException.InvalidLicenseException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -57,16 +60,27 @@ class MainActivity : ComponentActivity() { } } - // Step 2.3: Setup platform configuration with the MATTR VII tenant host. + // Step 2.3: Setup platform configuration with the MATTR VII tenant host and Verifier + // Application id. From Android Verifier SDK v7.0.0, the applicationId is supplied here (it is + // no longer passed to requestMobileCredentials) and also drives SDK Tethering. val platformConfiguration = PlatformConfiguration( - tenantHost = URL(Constants.TENANT_HOST) + tenantHost = URL(Constants.TENANT_HOST), + applicationId = Constants.APPLICATION_ID ) - // Step 2.4: Initialize the SDK. Must complete before any other SDK calls. + // Step 2.4: Initialize the SDK. Must complete before any other SDK calls. Initialization + // registers the app instance with the tenant and obtains a license, so it can throw + // FailedToRegisterException and InvalidLicenseException. lifecycleScope.launch { - MobileCredentialVerifier.initialize( - context = this@MainActivity, platformConfiguration = platformConfiguration - ) + try { + MobileCredentialVerifier.initialize( + context = this@MainActivity, platformConfiguration = platformConfiguration + ) + } catch (e: FailedToRegisterException) { + // Registration with the MATTR VII tenant failed — check connectivity and configuration. + } catch (e: InvalidLicenseException) { + // The SDK license is missing, invalid, or expired. + } } } } @@ -135,15 +149,23 @@ class VerifierViewModel : ViewModel() { // Step 3.2: Request credentials. The SDK launches the wallet via OID4VP and // suspends until the wallet redirects back via the deep link registered in // AndroidManifest.xml, at which point the response is returned here. + // From v7.0.0, applicationId is no longer passed here — it comes from the + // PlatformConfiguration supplied at initialization. val onlinePresentationResult = MobileCredentialVerifier.requestMobileCredentials( activity = activity, - request = listOf(mobileCredentialRequest), - applicationId = Constants.APPLICATION_ID + request = listOf(mobileCredentialRequest) ) - // Step 4.2: Surface the verified credentials to the UI. - _receivedDocuments.value = - onlinePresentationResult.mobileCredentialResponse?.credentials ?: emptyList() + // Step 4.2: Surface the verified credentials to the UI. From v7.0.0, + // OnlinePresentationSessionResult is a sealed interface with Success/Failure variants. + _receivedDocuments.value = when (onlinePresentationResult) { + is OnlinePresentationSessionResult.Success -> + onlinePresentationResult.mobileCredentialResponse?.credentials ?: emptyList() + is OnlinePresentationSessionResult.Failure -> { + // onlinePresentationResult.error is available here. + emptyList() + } + } } catch (e: Exception) { e.printStackTrace() } diff --git a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/Constants.swift b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/Constants.swift index 58f9c32..edfebcd 100644 --- a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/Constants.swift +++ b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/Constants.swift @@ -6,8 +6,15 @@ // Certificate Management screen. // // In a production app, you would manage these certificates dynamically rather than hardcoding them. +import Foundation + enum Constants { - static let montcliffPEM = + // From iOS Verifier SDK v6.0.0, the SDK is tethered to a MATTR VII tenant. Replace these with + // your tenant host and the Verifier Application `id` created on that tenant. + static let tenantHost = URL(string: "https://your-tenant.vii.mattr.global")! + static let applicationId = "" + + static let montcliffPEM = """ MIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG EwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew diff --git a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/ContentView.swift b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/ContentView.swift index 997a859..5772f4d 100644 --- a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/ContentView.swift +++ b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/ContentView.swift @@ -138,14 +138,25 @@ final class VerifierViewModel { // to prepare the SDK for use. A trusted IACA certificate is also pre-loaded so the app // can verify mDocs issued by the Montcliff DMV test issuer out of the box. init() { - do { - mobileCredentialVerifier = MobileCredentialVerifier.shared - try mobileCredentialVerifier.initialize() - Task { - try? await mobileCredentialVerifier.addTrustedIssuerCertificates(certificates: [Constants.montcliffPEM]) + mobileCredentialVerifier = MobileCredentialVerifier.shared + // From v6.0.0, initialize is asynchronous and requires a PlatformConfiguration. On first + // launch it registers this app instance with the tenant and obtains a license, so it can + // throw failedToRegister and invalidLicense. + Task { + do { + let platformConfiguration = PlatformConfiguration( + tenantHost: Constants.tenantHost, + applicationId: Constants.applicationId + ) + try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration) + try? await mobileCredentialVerifier.addTrustedIssuerCertificates(certificates: [Constants.montcliffPEM]) + } catch MobileCredentialVerifierError.failedToRegister { + // Registration with the MATTR VII tenant failed — check connectivity and configuration. + } catch MobileCredentialVerifierError.invalidLicense { + // The SDK license is missing, invalid, or expired. + } catch { + print(error.localizedDescription) } - } catch { - print(error.localizedDescription) } } } diff --git a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/DocumentView.swift b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/DocumentView.swift index c388c84..b4c9e1b 100644 --- a/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/DocumentView.swift +++ b/ios-in-person-verifier-tutorial-sample-app/In person verification tutorial/DocumentView.swift @@ -116,7 +116,8 @@ class DocumentViewModel { init(from presentation: MobileCredentialPresentation) { self.docType = presentation.docType self.verificationResult = presentation.verificationResult.verified ? "Verified" : "Invalid" - self.verificationFailedReason = presentation.verificationResult.reason?.message + // From v6.0.0, the failure detail is exposed via `failureType` (was `reason`). + self.verificationFailedReason = presentation.verificationResult.failureType?.message self.namespacesAndClaims = presentation.claims?.reduce(into: [String: [String: String]]()) { result, outerElement in let (outerKey, innerDict) = outerElement diff --git a/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/ContentView.swift b/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/ContentView.swift index f4115f1..6249e64 100644 --- a/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/ContentView.swift +++ b/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/ContentView.swift @@ -68,8 +68,11 @@ final class VerifierViewModel: ObservableObject { @Published var navigationPath = NavigationPath() @Published var receivedDocuments: [MobileCredentialPresentation] = [] + // From v6.0.0, the applicationId is supplied via PlatformConfiguration (it is no longer passed + // to requestMobileCredentials) and also drives SDK Tethering. let platformConfiguration = PlatformConfiguration( - tenantHost: Constants.tenantHost + tenantHost: Constants.tenantHost, + applicationId: Constants.applicationID ) var mobileCredentialVerifier: MobileCredentialVerifier @@ -95,11 +98,20 @@ final class VerifierViewModel: ObservableObject { // platform configuration (your tenant host). Trusted issuer certificates are managed // in your MATTR VII tenant rather than in the app for this workflow. init() { - do { - mobileCredentialVerifier = MobileCredentialVerifier.shared - try mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration) - } catch { - print(error.localizedDescription) + mobileCredentialVerifier = MobileCredentialVerifier.shared + // From v6.0.0, initialize is asynchronous and drives SDK Tethering: on first launch it + // registers this app instance with the tenant and obtains a license, so it can throw + // failedToRegister and invalidLicense. + Task { + do { + try await mobileCredentialVerifier.initialize(platformConfiguration: platformConfiguration) + } catch MobileCredentialVerifierError.failedToRegister { + // Registration with the MATTR VII tenant failed — check connectivity and configuration. + } catch MobileCredentialVerifierError.invalidLicense { + // The SDK license is missing, invalid, or expired. + } catch { + print(error.localizedDescription) + } } } @@ -111,17 +123,24 @@ final class VerifierViewModel: ObservableObject { Task { @MainActor in receivedDocuments = [] do { + // From v6.0.0, applicationId is no longer passed here (it comes from + // PlatformConfiguration) and challenge is optional — omit it to let the SDK generate + // a secure challenge, or pass your own to bind the response to this request. let onlinePresentationResult = try await mobileCredentialVerifier.requestMobileCredentials( request: [mobileCredentialRequest], - challenge: UUID().uuidString, - applicationId: Constants.applicationID + challenge: UUID().uuidString ) - guard let receivedCredentials = onlinePresentationResult.mobileCredentialResponse?.credentials else { - let errorMessage = onlinePresentationResult.error?.message ?? "No error message" - print("No response received: \(errorMessage)") + // From v6.0.0, OnlinePresentationSessionResult is a @frozen enum with success and + // failure cases, so branch over it with switch/case instead of inspecting nullable + // fields. success carries (sessionId, challenge, mobileCredentialResponse); failure + // carries (sessionId, challenge, error). + switch onlinePresentationResult { + case .success(_, _, let mobileCredentialResponse): + receivedDocuments = mobileCredentialResponse?.credentials ?? [] + case .failure(_, _, let error): + print("No response received: \(error.message)") return } - receivedDocuments = receivedCredentials } catch { print(error.localizedDescription) } diff --git a/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/DocumentView.swift b/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/DocumentView.swift index 5e25804..005b68b 100644 --- a/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/DocumentView.swift +++ b/ios-remote-verification-tutorial-sample-app/Remote verification tutorial/DocumentView.swift @@ -111,7 +111,8 @@ class DocumentViewModel: ObservableObject { init(from presentation: MobileCredentialPresentation) { self.docType = presentation.docType self.verificationResult = presentation.verificationResult.verified ? "Verified" : "Invalid" - self.verificationFailedReason = presentation.verificationResult.reason?.message + // From v6.0.0, the failure detail is exposed via `failureType` (was `reason`). + self.verificationFailedReason = presentation.verificationResult.failureType?.message self.namespacesAndClaims = presentation.claims?.reduce(into: [String: [String: String]]()) { result, outerElement in let (outerKey, innerDict) = outerElement