Skip to content

Commit d0cbbba

Browse files
@W-21933885: [MSDK Android] App Attestation Implementation (In-Line Retry For Expired Integrity Token Provider)
1 parent 6016a87 commit d0cbbba

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/auth/AppAttestationClient.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ class AppAttestationClient(
180180
}.getOrElse { e ->
181181
// If the Google Play Integrity API failed due to the Integrity Token Provider being expired, re-prepare it once for an inline retry.
182182
if ((e as? IntegrityServiceException)?.errorCode == INTEGRITY_TOKEN_PROVIDER_INVALID) {
183-
createSalesforceOAuthAuthorizationAppAttestation(integrityTokenProvider = null)
183+
createSalesforceOAuthAuthorizationAppAttestation(
184+
integrityManager = integrityManager,
185+
integrityTokenProvider = null
186+
)
184187
} else {
185188
null
186189
}

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/AppAttestationClientTest.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ package com.salesforce.androidsdk.auth
2929
import android.content.Context
3030
import androidx.test.ext.junit.runners.AndroidJUnit4
3131
import com.google.android.gms.tasks.Task
32+
import com.google.android.play.core.integrity.IntegrityServiceException
3233
import com.google.android.play.core.integrity.StandardIntegrityManager
3334
import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityToken
3435
import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenProvider
36+
import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID
3537
import com.salesforce.androidsdk.rest.RestClient
3638
import com.salesforce.androidsdk.rest.RestResponse
3739
import io.mockk.coEvery
@@ -181,6 +183,66 @@ class AppAttestationClientTest {
181183
assertEquals("eyJhdHRlc3RhdGlvbklkIjoiMTIzNDU2IiwiYXR0ZXN0YXRpb25EYXRhIjoiWDE5VVJWTlVYMGxPVkVWSFVrbFVXVjlVVDB0RlRsOWYifQ==", result)
182184
}
183185

186+
@OptIn(ExperimentalCoroutinesApi::class)
187+
@Test
188+
fun appAttestationClient_createSalesforceOAuthAuthorizationAppAttestationThrowingForInvalidIntegrityTokenProvider_returnsSuccessfully() = runTest {
189+
190+
val context = mockk<Context>(relaxed = true)
191+
val deviceId = "123456"
192+
val googleCloudProjectId = 654321L
193+
val remoteAccessConsumerKey = "13579"
194+
val restResponse = mockk<RestResponse>(relaxed = true)
195+
every { restResponse.asString() } returns "__TEST_CHALLENGE_VALUE__"
196+
every { restResponse.isSuccess } returns true
197+
val restClient = mockk<RestClient>(relaxed = true)
198+
every { restClient.sendSync(any()) } returns restResponse
199+
200+
val integrityToken = mockk<StandardIntegrityToken>(relaxed = true)
201+
every { integrityToken.token() } returns "__TEST_INTEGRITY_TOKEN__"
202+
val throwingIntegrityTokenTask = mockk<Task<StandardIntegrityToken>>(relaxed = true)
203+
every { throwingIntegrityTokenTask.addOnFailureListener(any()) } returns throwingIntegrityTokenTask
204+
every { throwingIntegrityTokenTask.getResult() } returns integrityToken
205+
mockkStatic("kotlinx.coroutines.tasks.TasksKt")
206+
val integrityServiceException = mockk<IntegrityServiceException>(relaxed = true)
207+
every { integrityServiceException.errorCode } returns INTEGRITY_TOKEN_PROVIDER_INVALID
208+
coEvery { throwingIntegrityTokenTask.await() } throws integrityServiceException
209+
val throwingIntegrityTokenProvider = mockk<StandardIntegrityTokenProvider>(relaxed = true)
210+
every { throwingIntegrityTokenProvider.request(any()) } returns throwingIntegrityTokenTask
211+
212+
val successfulIntegrityTokenTask = mockk<Task<StandardIntegrityToken>>(relaxed = true)
213+
every { successfulIntegrityTokenTask.addOnFailureListener(any()) } returns successfulIntegrityTokenTask
214+
every { successfulIntegrityTokenTask.getResult() } returns integrityToken
215+
coEvery { successfulIntegrityTokenTask.await() } returns integrityToken
216+
val successfulIntegrityTokenProvider = mockk<StandardIntegrityTokenProvider>(relaxed = true)
217+
every { successfulIntegrityTokenProvider.request(any()) } returns successfulIntegrityTokenTask
218+
219+
val integrityTokenProviderTask = mockk<Task<StandardIntegrityTokenProvider>>(relaxed = true)
220+
every { integrityTokenProviderTask.addOnSuccessListener(any()) } returns integrityTokenProviderTask
221+
every { integrityTokenProviderTask.addOnFailureListener(any()) } returns integrityTokenProviderTask
222+
coEvery { integrityTokenProviderTask.result } returns successfulIntegrityTokenProvider
223+
val integrityManager = mockk<StandardIntegrityManager>(relaxed = true)
224+
every { integrityManager.prepareIntegrityToken(any()) } returns integrityTokenProviderTask
225+
226+
val appAttestationClient = AppAttestationClient(
227+
apiHostName = "login.example.com",
228+
context = context,
229+
deviceId = deviceId,
230+
googleCloudProjectId = googleCloudProjectId,
231+
remoteAccessConsumerKey = remoteAccessConsumerKey,
232+
restClient = restClient
233+
)
234+
235+
val result = appAttestationClient.createSalesforceOAuthAuthorizationAppAttestation(
236+
integrityManager = integrityManager,
237+
integrityTokenProvider = throwingIntegrityTokenProvider
238+
)
239+
240+
advanceUntilIdle()
241+
242+
assertEquals("eyJhdHRlc3RhdGlvbklkIjoiMTIzNDU2IiwiYXR0ZXN0YXRpb25EYXRhIjoiWDE5VVJWTlVYMGxPVkVWSFVrbFVXVjlVVDB0RlRsOWYifQ==", result)
243+
}
244+
245+
184246
@OptIn(ExperimentalCoroutinesApi::class)
185247
@Test
186248
fun appAttestationClient_createSalesforceOAuthAuthorizationAppAttestationWhenIntegrityTokenProviderIsNull_returnsSuccessfully() = runTest {

0 commit comments

Comments
 (0)