diff --git a/README.md b/README.md index d4edb92..5fbf878 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The MIRACL Trust Android SDK provides the following functionalities: ```kotlin dependencies { - implementation("com.miracl:trust-sdk-android:1.13.0") + implementation("com.miracl:trust-sdk-android:1.14.0") } ``` @@ -38,7 +38,7 @@ The MIRACL Trust Android SDK provides the following functionalities: ```groovy dependencies { - implementation "com.miracl:trust-sdk-android:1.13.0" + implementation "com.miracl:trust-sdk-android:1.14.0" } ``` diff --git a/miracl-sdk/build.gradle b/miracl-sdk/build.gradle index a6a1f14..b0cc78a 100644 --- a/miracl-sdk/build.gradle +++ b/miracl-sdk/build.gradle @@ -13,7 +13,7 @@ plugins { alias libs.plugins.jreleaser } -version = '1.13.0' +version = '1.14.0' android { namespace 'com.miracl.trust' diff --git a/miracl-sdk/src/main/java/com/miracl/trust/MIRACLTrust.kt b/miracl-sdk/src/main/java/com/miracl/trust/MIRACLTrust.kt index 12d9f44..170fe7d 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/MIRACLTrust.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/MIRACLTrust.kt @@ -8,6 +8,7 @@ import com.miracl.trust.authentication.AuthenticatorScopes import com.miracl.trust.configuration.* import com.miracl.trust.configuration.factory.ConfigurationFactory import com.miracl.trust.configuration.factory.DefaultConfigurationFactory +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.delegate.PinProvider import com.miracl.trust.delegate.ResultHandler import com.miracl.trust.factory.ComponentFactory @@ -187,7 +188,12 @@ public class MIRACLTrust private constructor( configuration.applicationInfo ) - val componentFactory = configuration.componentFactory ?: ComponentFactory(context, logger) + var componentFactory = configuration.componentFactory + if (componentFactory == null) { + val deviceTagProvider = DeviceTagProvider.create(context) + componentFactory = ComponentFactory(context, logger, deviceTagProvider) + } + apiSettings = ApiSettings(projectUrl) miraclTrustCoroutineContext = configuration.miraclCoroutineContext diff --git a/miracl-sdk/src/main/java/com/miracl/trust/core/DeviceTagProvider.kt b/miracl-sdk/src/main/java/com/miracl/trust/core/DeviceTagProvider.kt new file mode 100644 index 0000000..c456059 --- /dev/null +++ b/miracl-sdk/src/main/java/com/miracl/trust/core/DeviceTagProvider.kt @@ -0,0 +1,75 @@ +package com.miracl.trust.core + +import android.content.Context +import com.miracl.trust.util.toHexString +import java.io.File +import java.security.SecureRandom + +internal class DeviceTagProvider(private val file: File) { + + fun get(): String { + cachedTag?.let { return it } + + synchronized(LOCK) { + cachedTag?.let { return it } + + val tag = try { + if (file.exists()) { + val stored = file.readText(Charsets.UTF_8).trim() + + if (isValidTag(stored)) { + stored + } else { + createAndPersist(file) + } + } else { + createAndPersist(file) + } + } catch (_: Exception) { + createAndPersist(file) + } + + cachedTag = tag + return tag + } + } + + private fun createAndPersist(file: File): String { + val tag = generateTag() + + try { + file.writeText(tag, Charsets.UTF_8) + } catch (_: Exception) { + // Ignore persistence failure. + } + + return tag + } + + private fun isValidTag(value: String): Boolean { + return value.length == ID_LENGTH && + value.all { it in '0'..'9' || it in 'a'..'f' } + } + + private fun generateTag(): String { + val data = ByteArray(16) + SecureRandom().nextBytes(data) + + return data.toHexString() + } + + internal companion object { + private const val FILE_NAME = "miracl_device_tag" + private const val ID_LENGTH = 32 + + private val LOCK = Any() + + @Volatile + private var cachedTag: String? = null + + fun create(context: Context): DeviceTagProvider { + val file = File(context.applicationContext.noBackupFilesDir, FILE_NAME) + return DeviceTagProvider(file) + } + } +} \ No newline at end of file diff --git a/miracl-sdk/src/main/java/com/miracl/trust/factory/ComponentFactory.kt b/miracl-sdk/src/main/java/com/miracl/trust/factory/ComponentFactory.kt index a101d6c..288eb98 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/factory/ComponentFactory.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/factory/ComponentFactory.kt @@ -4,6 +4,7 @@ import android.content.Context import com.miracl.trust.authentication.AuthenticationApi import com.miracl.trust.authentication.Authenticator import com.miracl.trust.authentication.AuthenticatorContract +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.crypto.Crypto import com.miracl.trust.registration.* import com.miracl.trust.session.* @@ -14,7 +15,8 @@ import com.miracl.trust.util.log.Logger internal class ComponentFactory( private val context: Context, - private val logger: Logger + private val logger: Logger, + private val deviceTagProvider: DeviceTagProvider ) { private val crypto: Crypto = Crypto(logger) @@ -25,7 +27,8 @@ internal class ComponentFactory( authenticator: AuthenticatorContract, verificationApi: VerificationApi, userStorage: UserStorage - ): Verificator = Verificator(authenticator, verificationApi, userStorage, logger) + ): Verificator = + Verificator(authenticator, verificationApi, userStorage, logger, deviceTagProvider) fun createRegistrator( registrationApi: RegistrationApi, @@ -35,7 +38,8 @@ internal class ComponentFactory( registrationApi, crypto, userStorage, - logger + logger, + deviceTagProvider ) fun createAuthenticator( diff --git a/miracl-sdk/src/main/java/com/miracl/trust/registration/Registration.kt b/miracl-sdk/src/main/java/com/miracl/trust/registration/Registration.kt index 34cdb39..066b192 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/registration/Registration.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/registration/Registration.kt @@ -4,6 +4,7 @@ import androidx.annotation.VisibleForTesting import com.miracl.trust.MIRACLError import com.miracl.trust.MIRACLResult import com.miracl.trust.MIRACLSuccess +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.crypto.Crypto import com.miracl.trust.crypto.CryptoException import com.miracl.trust.crypto.SigningKeyPair @@ -35,7 +36,8 @@ internal class Registrator( private val registrationApi: RegistrationApi, private val crypto: Crypto, private val userStorage: UserStorage, - private val logger: Logger + private val logger: Logger, + private val deviceTagProvider: DeviceTagProvider ) : RegistratorContract { companion object { internal const val MIN_PIN_LENGTH = 4 @@ -74,7 +76,8 @@ internal class Registrator( deviceName = deviceName.trim(), activationToken = activationToken.trim(), pushToken = pushNotificationsToken, - publicKey = signingKeyPair.publicKey.toHexString() + publicKey = signingKeyPair.publicKey.toHexString(), + deviceTag = deviceTagProvider.get() ) try { diff --git a/miracl-sdk/src/main/java/com/miracl/trust/registration/RegistrationApi.kt b/miracl-sdk/src/main/java/com/miracl/trust/registration/RegistrationApi.kt index 1b2cce3..82f89c0 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/registration/RegistrationApi.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/registration/RegistrationApi.kt @@ -18,7 +18,8 @@ internal data class RegisterRequestBody( @SerialName("deviceName") val deviceName: String, @SerialName("activationToken") val activationToken: String, @SerialName("pushToken") val pushToken: String? = null, - @SerialName("publicKey") val publicKey: String + @SerialName("publicKey") val publicKey: String, + @SerialName("deviceTag") val deviceTag: String ) @Serializable diff --git a/miracl-sdk/src/main/java/com/miracl/trust/registration/VerificationApi.kt b/miracl-sdk/src/main/java/com/miracl/trust/registration/VerificationApi.kt index d9e66ca..623d7ad 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/registration/VerificationApi.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/registration/VerificationApi.kt @@ -44,7 +44,8 @@ internal data class QuickCodeVerificationResponse( @Serializable internal data class ConfirmationRequestBody( val userId: String, - val code: String + val code: String, + val deviceTag: String ) @Serializable diff --git a/miracl-sdk/src/main/java/com/miracl/trust/registration/Verificator.kt b/miracl-sdk/src/main/java/com/miracl/trust/registration/Verificator.kt index d87ffed..30a7f25 100644 --- a/miracl-sdk/src/main/java/com/miracl/trust/registration/Verificator.kt +++ b/miracl-sdk/src/main/java/com/miracl/trust/registration/Verificator.kt @@ -7,6 +7,7 @@ import com.miracl.trust.MIRACLSuccess import com.miracl.trust.authentication.AuthenticationException import com.miracl.trust.authentication.AuthenticatorContract import com.miracl.trust.authentication.AuthenticatorScopes +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.delegate.PinProvider import com.miracl.trust.model.QuickCode import com.miracl.trust.model.User @@ -21,7 +22,8 @@ internal class Verificator( private val authenticator: AuthenticatorContract, private val verificationApi: VerificationApi, private val userStorage: UserStorage, - private val logger: Logger + private val logger: Logger, + private val deviceTagProvider: DeviceTagProvider ) { suspend fun sendVerificationEmail( userId: String, @@ -133,8 +135,9 @@ internal class Verificator( } val confirmationRequestBody = ConfirmationRequestBody( - userId, - code + userId = userId, + code = code, + deviceTag = deviceTagProvider.get() ) logOperation(LoggerConstants.VerificatorOperations.ACTIVATION_TOKEN_REQUEST) diff --git a/miracl-sdk/src/test/java/com/miracl/trust/MIRACLTrustUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/MIRACLTrustUnitTest.kt index 92ae555..211f4c3 100644 --- a/miracl-sdk/src/test/java/com/miracl/trust/MIRACLTrustUnitTest.kt +++ b/miracl-sdk/src/test/java/com/miracl/trust/MIRACLTrustUnitTest.kt @@ -110,6 +110,7 @@ class MIRACLTrustUnitTest { // Arrange val userStorageMock = mockk() val config = Configuration.Builder(projectId, projectUrl) + .componentFactory(componentFactoryMock) .deviceName(deviceName) .userStorage(userStorageMock) .build() @@ -173,7 +174,11 @@ class MIRACLTrustUnitTest { @Test fun `createInstance creates configuration when there isn't any`() = runTest { // Arrange - val configuration = Configuration.Builder().deviceName(deviceName).build() + val configuration = Configuration.Builder() + .componentFactory(componentFactoryMock) + .deviceName(deviceName) + .build() + val configurationFactory = mockk() every { configurationFactory.create() } returns configuration @@ -333,6 +338,7 @@ class MIRACLTrustUnitTest { val userStorageMock = mockk() val config = Configuration.Builder(projectId, projectUrl) .deviceName(deviceName) + .componentFactory(componentFactoryMock) .userStorage(userStorageMock) .build() @@ -358,6 +364,7 @@ class MIRACLTrustUnitTest { every { userStorageMock.all() } returns listOf() val config = Configuration.Builder(projectId, projectUrl) + .componentFactory(componentFactoryMock) .deviceName(deviceName) .build() diff --git a/miracl-sdk/src/test/java/com/miracl/trust/core/DeviceTagProviderUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/core/DeviceTagProviderUnitTest.kt new file mode 100644 index 0000000..7a58523 --- /dev/null +++ b/miracl-sdk/src/test/java/com/miracl/trust/core/DeviceTagProviderUnitTest.kt @@ -0,0 +1,167 @@ +package com.miracl.trust.core + +import com.miracl.trust.randomHexString +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File + +class DeviceTagProviderUnitTest { + + @get:Rule + val tempFolder = TemporaryFolder() + + private lateinit var file: File + private lateinit var provider: DeviceTagProvider + + @Before + fun setup() { + file = tempFolder.newFile("miracl_device_tag") + + if (file.exists()) { + file.delete() + } + + val cacheField = DeviceTagProvider::class.java.getDeclaredField("cachedTag").apply { + isAccessible = true + } + cacheField.set(null, null) + + provider = DeviceTagProvider(file) + } + + @After + fun tearDown() { + if (file.exists()) { + file.setWritable(true) + file.setReadable(true) + } + } + + @Test + fun `get should return existing valid tag without modifying it when tag already exists on disk`() { + // Arrange + val expectedTag = "1234567890abcdef1234567890abcdef" + file.writeText(expectedTag) + + // Act + val result = provider.get() + + // Assert + Assert.assertEquals(expectedTag, result) + Assert.assertEquals(expectedTag, file.readText().trim()) + } + + @Test + fun `get should generate and persist a new tag when the file does not exist`() { + // Arrange & Act + val result = provider.get() + + // Assert + Assert.assertEquals(32, result.length) + Assert.assertTrue(result.all { it in '0'..'9' || it in 'a'..'f' }) + Assert.assertTrue(file.exists()) + } + + @Test + fun `get should return the exact same tag on consecutive invocations`() { + // Arrange + val firstCallTag = provider.get() + + // Act + val secondCallTag = provider.get() + + // Assert + Assert.assertEquals(firstCallTag, secondCallTag) + } + + @Test + fun `get should return the same tag across different provider instances pointing to the same file`() { + // Arrange + val expectedTag = provider.get() + val secondProvider = DeviceTagProvider(file) + + // Act + val result = secondProvider.get() + + // Assert + Assert.assertEquals(expectedTag, result) + } + + @Test + fun `get should return the tag from the shared process cache without touching the disk when a second instance is initialized`() { + // Arrange + val firstProvider = DeviceTagProvider(file) + val expectedTag = firstProvider.get() + val secondProvider = DeviceTagProvider(file) + + // Act + file.delete() + val result = secondProvider.get() + + // Assert + Assert.assertEquals(expectedTag, result) + } + + @Test + fun `get should regenerate and persist a fresh tag when the existing file is corrupted`() { + // Arrange + val corruptedData = "INVALID_DATA" + file.writeText(corruptedData) + + // Act + val result = provider.get() + + // Assert + Assert.assertEquals(32, result.length) + Assert.assertTrue(result.all { it in '0'..'9' || it in 'a'..'f' }) + Assert.assertNotEquals(corruptedData, result) + Assert.assertEquals(result, file.readText().trim()) + } + + @Test + fun `get should regenerate and persist a fresh tag when the existing file is empty`() { + // Arrange + file.writeText("") + + // Act + val result = provider.get() + + // Assert + Assert.assertEquals(32, result.length) + Assert.assertTrue(result.all { it in '0'..'9' || it in 'a'..'f' }) + Assert.assertEquals(result, file.readText().trim()) + } + + @Test + fun `get should fallback to a volatile runtime tag and not crash when the file is not writable`() { + // Arrange + file.createNewFile() + file.setWritable(false) + + // Act + val result = provider.get() + + // Assert + Assert.assertEquals(32, result.length) + Assert.assertTrue(result.all { it in '0'..'9' || it in 'a'..'f' }) + } + + @Test + fun `get should fallback to a volatile runtime tag and not crash when reading the file fails`() { + // Arrange + file.writeText("1234567890abcdef1234567890abcdef") + randomHexString() + file.setReadable(false) + + // Act + val result = provider.get() + + // Assert + Assert.assertEquals(32, result.length) + Assert.assertTrue(result.all { it in '0'..'9' || it in 'a'..'f' }) + } +} \ No newline at end of file diff --git a/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistrationApiUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistrationApiUnitTest.kt index 1108848..784b97c 100644 --- a/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistrationApiUnitTest.kt +++ b/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistrationApiUnitTest.kt @@ -47,7 +47,8 @@ class RegistrationApiUnitTest { userId = randomUuidString(), deviceName = randomUuidString(), activationToken = randomUuidString(), - publicKey = randomHexString() + publicKey = randomHexString(), + deviceTag = randomHexString() ) val capturingSlot = CapturingSlot() val registerResponse = RegisterResponse( @@ -83,7 +84,8 @@ class RegistrationApiUnitTest { userId = randomUuidString(), deviceName = randomUuidString(), activationToken = randomUuidString(), - publicKey = randomHexString() + publicKey = randomHexString(), + deviceTag = randomHexString() ) val jsonString = "invalid json" val executorResult = MIRACLSuccess( @@ -111,7 +113,8 @@ class RegistrationApiUnitTest { userId = randomUuidString(), deviceName = randomUuidString(), activationToken = randomUuidString(), - publicKey = randomHexString() + publicKey = randomHexString(), + deviceTag = randomHexString() ) val httpRequestExecutorException = ApiException.ExecutionError() val executorResult = MIRACLError( @@ -139,7 +142,8 @@ class RegistrationApiUnitTest { userId = randomUuidString(), deviceName = randomUuidString(), activationToken = randomUuidString(), - publicKey = randomHexString() + publicKey = randomHexString(), + deviceTag = randomHexString() ) val apiException = @@ -171,7 +175,8 @@ class RegistrationApiUnitTest { userId = randomUuidString(), deviceName = randomUuidString(), activationToken = randomUuidString(), - publicKey = randomHexString() + publicKey = randomHexString(), + deviceTag = randomHexString() ) val exceptionMessage = "Unexpected exception" val exception = Exception(exceptionMessage) diff --git a/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistratorUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistratorUnitTest.kt index 45447bb..3cadfc9 100644 --- a/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistratorUnitTest.kt +++ b/miracl-sdk/src/test/java/com/miracl/trust/registration/RegistratorUnitTest.kt @@ -3,6 +3,7 @@ package com.miracl.trust.registration import com.miracl.trust.MIRACLError import com.miracl.trust.MIRACLSuccess import com.miracl.trust.assertUserEqualsDto +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.crypto.Crypto import com.miracl.trust.crypto.CryptoException import com.miracl.trust.crypto.SigningKeyPair @@ -36,6 +37,7 @@ class RegistratorUnitTest { private val userId = randomUuidString() private val projectId = randomUuidString() private val deviceName = randomUuidString() + private val deviceTag = randomHexString() private val pin = randomNumericPin(randomPinLength()) private val pinProvider = PinProvider { it.consume(pin) } @@ -43,6 +45,7 @@ class RegistratorUnitTest { private val cryptoMock = mockk() private val userStorageMock = mockk() private val logger = DefaultLogger(loggingLevel = Logger.LoggingLevel.NONE) + private val deviceTagProvider = mockk() private lateinit var registrator: Registrator @@ -52,7 +55,10 @@ class RegistratorUnitTest { setUpRegistrationApiMock() setUpCryptoMock() - registrator = Registrator(registrationApiMock, cryptoMock, userStorageMock, logger) + every { deviceTagProvider.get() } returns deviceTag + + registrator = + Registrator(registrationApiMock, cryptoMock, userStorageMock, logger, deviceTagProvider) } @Test @@ -118,6 +124,7 @@ class RegistratorUnitTest { signingKeyPair.publicKey.toHexString(), registerRequestBody.publicKey ) + Assert.assertEquals(deviceTag, registerRequestBody.deviceTag) } @Test diff --git a/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificationApiUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificationApiUnitTest.kt index 60fdf2b..52ddc5a 100644 --- a/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificationApiUnitTest.kt +++ b/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificationApiUnitTest.kt @@ -351,7 +351,8 @@ class VerificationApiUnitTest { // Arrange val confirmationRequestBody = ConfirmationRequestBody( userId = randomUuidString(), - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) val confirmationRequestBodyAsJson = @@ -399,7 +400,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = randomUuidString(), - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) @@ -432,7 +434,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = userId, - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) @@ -465,7 +468,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = randomUuidString(), - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) @@ -499,7 +503,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = userId, - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) @@ -532,7 +537,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = randomUuidString(), - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) @@ -554,7 +560,8 @@ class VerificationApiUnitTest { val result = apiManager.executeConfirmationRequest( ConfirmationRequestBody( userId = randomUuidString(), - code = randomUuidString() + code = randomUuidString(), + deviceTag = randomHexString() ) ) diff --git a/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificatorUnitTest.kt b/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificatorUnitTest.kt index b62692c..4e6b125 100644 --- a/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificatorUnitTest.kt +++ b/miracl-sdk/src/test/java/com/miracl/trust/registration/VerificatorUnitTest.kt @@ -7,9 +7,11 @@ import com.miracl.trust.authentication.AuthenticateResponse import com.miracl.trust.authentication.AuthenticationException import com.miracl.trust.authentication.AuthenticatorContract import com.miracl.trust.authentication.AuthenticatorScopes +import com.miracl.trust.core.DeviceTagProvider import com.miracl.trust.delegate.PinProvider import com.miracl.trust.model.User import com.miracl.trust.randomByteArray +import com.miracl.trust.randomHexString import com.miracl.trust.randomPinLength import com.miracl.trust.randomUuidString import com.miracl.trust.session.AuthenticationSessionDetails @@ -35,14 +37,22 @@ class VerificatorUnitTest { private val verificationApiMock = mockk() private val userStorageMock = mockk() private val logger = DefaultLogger(loggingLevel = Logger.LoggingLevel.NONE) + private val deviceTagProvider = mockk() private val verificator = - Verificator(authenticatorMock, verificationApiMock, userStorageMock, logger) + Verificator( + authenticatorMock, + verificationApiMock, + userStorageMock, + logger, + deviceTagProvider + ) @Before fun setUp() { clearAllMocks() every { userStorageMock.getUser(any(), any()) } returns null + every { deviceTagProvider.get() } returns randomHexString() } @Test @@ -528,6 +538,10 @@ class VerificatorUnitTest { // Assert Assert.assertEquals(userId, capturingSlotConfirmation.captured.userId) Assert.assertEquals(code, capturingSlotConfirmation.captured.code) + Assert.assertEquals( + deviceTagProvider.get(), + capturingSlotConfirmation.captured.deviceTag + ) Assert.assertTrue(result is MIRACLSuccess) Assert.assertEquals(