Skip to content

Commit f984d45

Browse files
@W-21933885: [MSDK Android] App Attestation Implementation (Automated Implementation Of New Tests In LoginViewModelTest.kt)
1 parent 7e5c233 commit f984d45

2 files changed

Lines changed: 76 additions & 1 deletion

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ open class LoginViewModel(val bootConfig: BootConfig) : ViewModel() {
467467
val codeChallenge = getSHA256Hash(codeVerifier)
468468

469469
// Populate the additional parameter map with app attestation, if applicable.
470-
SalesforceSDKManager.getInstance().appAttestationClient?.run {
470+
sdkManager.appAttestationClient?.run {
471471
val challenge = fetchMobileAppAttestationChallenge()
472472
val attestation = createAppAttestation(challenge) ?: return@run
473473
additionalParams?.put(ATTESTATION, attestation)

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import com.salesforce.androidsdk.config.OAuthConfig
4141
import com.salesforce.androidsdk.security.SalesforceKeyGenerator.getSHA256Hash
4242
import com.salesforce.androidsdk.ui.LoginActivity.Companion.ABOUT_BLANK
4343
import com.salesforce.androidsdk.ui.LoginViewModel
44+
import io.mockk.coEvery
4445
import io.mockk.coVerify
4546
import io.mockk.every
4647
import io.mockk.mockk
@@ -69,6 +70,10 @@ import java.net.URI
6970
private const val FAKE_SERVER_URL = "shouldMatchNothing.salesforce.com"
7071
private const val FAKE_JWT = "1234"
7172
private const val FAKE_JWT_FLOW_AUTH = "5678"
73+
private const val TEST_ATTESTATION_SERVER = "test.salesforce.com"
74+
private const val TEST_CHALLENGE_VALUE = "__TEST_CHALLENGE_VALUE__"
75+
private const val TEST_APP_ATTESTATION = "__TEST_APP_ATTESTATION__"
76+
private const val ATTESTATION_QUERY_PARAM_PREFIX = "attestation="
7277

7378
@OptIn(ExperimentalCoroutinesApi::class)
7479
@RunWith(AndroidJUnit4::class)
@@ -373,6 +378,7 @@ class LoginViewModelTest {
373378
scopes = listOf("api"),
374379
)
375380
}
381+
every { sdkManagerMock.appAttestationClient } returns null
376382
val debugConsumerKey = "debug_override_key_789"
377383
val debugRedirectUri = "debug://redirect"
378384
val debugScopes = listOf("api", "debug_scope")
@@ -418,6 +424,7 @@ class LoginViewModelTest {
418424
scopes = listOf("api"),
419425
)
420426
}
427+
every { sdkManagerMock.appAttestationClient } returns null
421428
val debugConsumerKey = "debug_override_key_789"
422429
val debugRedirectUri = "debug://redirect"
423430
val debugScopes = listOf("api", "debug_scope")
@@ -674,6 +681,55 @@ class LoginViewModelTest {
674681
}
675682
}
676683

684+
@Test
685+
fun getAuthorizationUrl_WithNullAppAttestationClient_OmitsAttestationParam() = runBlocking {
686+
val sdkManagerMock = createSdkManagerMockForAttestation(appAttestationClient = null)
687+
val freshViewModel = LoginViewModel(bootConfig)
688+
689+
val loginUrl = freshViewModel.getAuthorizationUrl(TEST_ATTESTATION_SERVER, sdkManagerMock)
690+
691+
assertFalse(
692+
"URL should NOT contain an attestation parameter but was '$loginUrl'.",
693+
loginUrl.contains(ATTESTATION_QUERY_PARAM_PREFIX),
694+
)
695+
}
696+
697+
@Test
698+
fun getAuthorizationUrl_WithAppAttestationClient_IncludesAttestationParam() = runBlocking {
699+
val appAttestationClient = createMockAppAttestationClient(attestation = TEST_APP_ATTESTATION)
700+
val sdkManagerMock = createSdkManagerMockForAttestation(appAttestationClient = appAttestationClient)
701+
val freshViewModel = LoginViewModel(bootConfig)
702+
703+
val loginUrl = freshViewModel.getAuthorizationUrl(TEST_ATTESTATION_SERVER, sdkManagerMock)
704+
705+
assertTrue(
706+
"URL should contain '$ATTESTATION_QUERY_PARAM_PREFIX$TEST_APP_ATTESTATION' but was '$loginUrl'.",
707+
loginUrl.contains("$ATTESTATION_QUERY_PARAM_PREFIX$TEST_APP_ATTESTATION"),
708+
)
709+
coVerify(exactly = 1) {
710+
appAttestationClient.fetchMobileAppAttestationChallenge()
711+
appAttestationClient.createAppAttestation(appAttestationChallenge = TEST_CHALLENGE_VALUE)
712+
}
713+
}
714+
715+
@Test
716+
fun getAuthorizationUrl_WhenCreateAppAttestationReturnsNull_OmitsAttestationParam() = runBlocking {
717+
val appAttestationClient = createMockAppAttestationClient(attestation = null)
718+
val sdkManagerMock = createSdkManagerMockForAttestation(appAttestationClient = appAttestationClient)
719+
val freshViewModel = LoginViewModel(bootConfig)
720+
721+
val loginUrl = freshViewModel.getAuthorizationUrl(TEST_ATTESTATION_SERVER, sdkManagerMock)
722+
723+
assertFalse(
724+
"URL should NOT contain an attestation parameter but was '$loginUrl'.",
725+
loginUrl.contains(ATTESTATION_QUERY_PARAM_PREFIX),
726+
)
727+
coVerify(exactly = 1) {
728+
appAttestationClient.fetchMobileAppAttestationChallenge()
729+
appAttestationClient.createAppAttestation(appAttestationChallenge = TEST_CHALLENGE_VALUE)
730+
}
731+
}
732+
677733
@Test
678734
fun loginViewModel_applyPendingLoginServer_returns_onNullPendingLoginServer() {
679735

@@ -1272,6 +1328,25 @@ class LoginViewModelTest {
12721328
assertEquals(result, viewModel.getValidServerUrl(value))
12731329
}
12741330

1331+
private fun createSdkManagerMockForAttestation(
1332+
appAttestationClient: AppAttestationClient?,
1333+
): SalesforceSDKManager = mockk<SalesforceSDKManager>(relaxed = true).also { mock ->
1334+
every { mock.useHybridAuthentication } returns false
1335+
every { mock.isDebugBuild } returns false
1336+
every { mock.debugOverrideAppConfig } returns null
1337+
every { mock.appConfigForLoginHost } returns { _ -> null }
1338+
every { mock.appAttestationClient } returns appAttestationClient
1339+
}
1340+
1341+
private fun createMockAppAttestationClient(
1342+
attestation: String?,
1343+
): AppAttestationClient = mockk<AppAttestationClient>(relaxed = true).also { client ->
1344+
every { client.fetchMobileAppAttestationChallenge() } returns TEST_CHALLENGE_VALUE
1345+
coEvery {
1346+
client.createAppAttestation(appAttestationChallenge = TEST_CHALLENGE_VALUE)
1347+
} returns attestation
1348+
}
1349+
12751350
private fun generateExpectedAuthorizationUrl(
12761351
server: String,
12771352
codeChallenge: String,

0 commit comments

Comments
 (0)