Skip to content

Commit

Permalink
test: added unit tests for synchronization of activity across multipl…
Browse files Browse the repository at this point in the history
…e instances of SecureCredentialsManager
  • Loading branch information
desusai7 committed Jul 26, 2024
1 parent 74774f8 commit a64a340
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 5 deletions.
5 changes: 5 additions & 0 deletions auth0/src/main/java/com/auth0/android/Auth0.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ public open class Auth0 private constructor(
instances.clear()
}

@JvmStatic
public fun hasInstance(clientId: String, domain: String): Boolean {
return instances.containsKey(Pair(clientId, domain))
}

private fun getResourceFromContext(context: Context, resName: String): String {
val stringRes = context.resources.getIdentifier(resName, "string", context.packageName)
require(stringRes != 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,13 +633,18 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT

internal companion object {
private val TAG = SecureCredentialsManager::class.java.simpleName
private const val KEY_CREDENTIALS = "com.auth0.credentials"
private const val KEY_EXPIRES_AT = "com.auth0.credentials_access_token_expires_at"
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_CREDENTIALS = "com.auth0.credentials"
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_EXPIRES_AT = "com.auth0.credentials_access_token_expires_at"

// This is no longer used as we get the credentials expiry from the access token only,
// but we still store it so users can rollback to versions where it is required.
private const val LEGACY_KEY_CACHE_EXPIRES_AT = "com.auth0.credentials_expires_at"
private const val KEY_CAN_REFRESH = "com.auth0.credentials_can_refresh"
private const val KEY_ALIAS = "com.auth0.key"
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val LEGACY_KEY_CACHE_EXPIRES_AT = "com.auth0.credentials_expires_at"
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_CAN_REFRESH = "com.auth0.credentials_can_refresh"
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val KEY_ALIAS = "com.auth0.key"
}
}
16 changes: 16 additions & 0 deletions auth0/src/test/java/com/auth0/android/Auth0Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,20 @@ public void shouldEnsureLogoutUrlIsOpen() throws NoSuchMethodException {
Assert.assertTrue(Modifier.isPublic(method.getModifiers()));
Assert.assertFalse(Modifier.isFinal(method.getModifiers()));
}

@Test
public void sameConfigShouldReturnSameInstance() {
Auth0 auth0 = Auth0.getInstance(CLIENT_ID, DOMAIN);
Auth0 auth0_2 = Auth0.getInstance(CLIENT_ID, DOMAIN);
Assert.assertSame(auth0, auth0_2);
}

@Test
public void clearInstanceShouldRemoveInstance() {
Auth0 auth0 = Auth0.getInstance(CLIENT_ID, DOMAIN);
Auth0.clearInstance(CLIENT_ID, DOMAIN);
Assert.assertFalse(Auth0.hasInstance(CLIENT_ID, DOMAIN));
Auth0 auth0_2 = Auth0.getInstance(CLIENT_ID, DOMAIN);
Assert.assertNotSame(auth0, auth0_2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2400,6 +2400,27 @@ public class AuthenticationAPIClientTest {
)
}

@Test
public fun diffClientInstancesFromSameAuth0InstanceShouldUseSameExecutor() {
val auth0 = Auth0.getInstance("clientId", "domain")
val client1 = AuthenticationAPIClient(auth0)
val client2 = AuthenticationAPIClient(auth0)
assertThat(client1.executor, Matchers.`is`(client2.executor))
assertThat(client1.executor, Matchers.`is`(auth0.executor))
assertThat(client2.executor, Matchers.`is`(auth0.executor))
}

@Test
public fun diffClientInstancesFromDiffAuth0InstancesShouldUseDiffExecutor() {
val auth01 = Auth0.getInstance("clientId1", "domain1")
val auth02 = Auth0.getInstance("clientId2", "domain2")
val client1 = AuthenticationAPIClient(auth01)
val client2 = AuthenticationAPIClient(auth02)
assertThat(client1.executor, Matchers.`is`(auth01.executor))
assertThat(client2.executor, Matchers.`is`(auth02.executor))
assertThat(client1.executor, Matchers.not(Matchers.`is`(client2.executor)))
}

private fun <T> bodyFromRequest(request: RecordedRequest): Map<String, T> {
val mapType = object : TypeToken<Map<String?, T>?>() {}.type
return gson.fromJson(request.body.readUtf8(), mapType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import androidx.fragment.app.FragmentActivity
import com.auth0.android.Auth0
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.authentication.storage.SecureCredentialsManager.Companion.KEY_CAN_REFRESH
import com.auth0.android.authentication.storage.SecureCredentialsManager.Companion.KEY_CREDENTIALS
import com.auth0.android.authentication.storage.SecureCredentialsManager.Companion.KEY_EXPIRES_AT
import com.auth0.android.authentication.storage.SecureCredentialsManager.Companion.LEGACY_KEY_CACHE_EXPIRES_AT
import com.auth0.android.callback.Callback
import com.auth0.android.request.Request
import com.auth0.android.request.internal.GsonProvider
Expand Down Expand Up @@ -1410,6 +1414,10 @@ public class SecureCredentialsManagerTest {
jwtDecoder,
auth0.executor
)
// lets add some delay if the $it is even, to create a complex scenario
if (it % 2 == 0) {
Thread.sleep(1000)
}
secureCredentialsManager.saveCredentials(credentials)
lastSavedCredentials = credentials
secureCredentialsManager.getCredentials(object :
Expand Down Expand Up @@ -1449,6 +1457,64 @@ public class SecureCredentialsManagerTest {
latch.await()
}

@Test
public fun shouldSynchronizeHasValidCredentialsFromMultipleThreadsAndInstances() {
val auth0 = Auth0.getInstance("clientId", "domain")
val executor: ExecutorService = Executors.newFixedThreadPool(5)
val context: Context =
Robolectric.buildActivity(Activity::class.java).create().start().resume().get()
val storage = SharedPreferencesStorage(
context = context,
sharedPreferencesName = "com.auth0.android.storage.SecureCredentialsManagerTest"
)
storage.remove(KEY_CREDENTIALS)
storage.remove(KEY_EXPIRES_AT)
storage.remove(LEGACY_KEY_CACHE_EXPIRES_AT)
storage.remove(KEY_CAN_REFRESH)
val cryptoMock = Mockito.mock(CryptoUtil::class.java)
Mockito.`when`(cryptoMock.encrypt(any())).thenAnswer {
val input = it.arguments[0] as ByteArray
input
}
Mockito.`when`(cryptoMock.decrypt(any())).thenAnswer {
val input = it.arguments[0] as ByteArray
input
}
val latch = CountDownLatch(5)
var lastSavedCredentials: Credentials?
repeat(5) {
executor.submit {
val credentials = Credentials(
idToken = "idToken$it",
accessToken = "accessToken$it",
type = "type$it",
refreshToken = "",
expiresAt = Date(
if (it % 2 == 0) CredentialsMock.CURRENT_TIME_MS else CredentialsMock.ONE_HOUR_AHEAD_MS
),
scope = "scope$it"
)
val secureCredentialsManager =
SecureCredentialsManager(
client,
storage,
cryptoMock,
jwtDecoder,
auth0.executor
)
secureCredentialsManager.saveCredentials(credentials)
lastSavedCredentials = credentials
val hasValidCredentials = secureCredentialsManager.hasValidCredentials()
MatcherAssert.assertThat(
hasValidCredentials,
Is.`is`(lastSavedCredentials!!.expiresAt.time > CredentialsMock.CURRENT_TIME_MS)
)
latch.countDown()
}
}
latch.await()
}

/*
* CLEAR Credentials tests
*/
Expand Down

0 comments on commit a64a340

Please sign in to comment.