From 8c1c90062a3e3d915e3d7fa4df65713aa3b79415 Mon Sep 17 00:00:00 2001 From: Ihor Monochkov Date: Wed, 24 Apr 2024 12:37:52 +0300 Subject: [PATCH] Fixed https://github.com/auth0/Auth0.Android/issues/726 --- .../storage/CredentialsManager.kt | 5 +- .../storage/CredentialsManagerTest.kt | 178 +++++++++++++++++- 2 files changed, 179 insertions(+), 4 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 2efe8537..555d4315 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -284,8 +284,9 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } val request = authenticationClient.renewAuth(refreshToken) request.addParameters(parameters) - if (scope != null) { - request.addParameter("scope", scope) + val scopeForRenew = scope ?: storedScope + if (scopeForRenew != null) { + request.addParameter("scope", scopeForRenew) } for (header in headers) { diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt index 19cd0cbf..3289c208 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt @@ -555,6 +555,180 @@ public class CredentialsManagerTest { MatcherAssert.assertThat(retrievedCredentials.scope, Is.`is`("newScope")) } + @Test + public fun shouldRenewExpiredCredentialsIfSavedScopeIsNotNullAndRequiredScopeIsNull() { + Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken") + Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken") + Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") + Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type") + val expirationTime = CredentialsMock.CURRENT_TIME_MS // expired credentials + Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime) + Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at")) + .thenReturn(expirationTime) + Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn("saved scope") + Mockito.`when`( + client.renewAuth("refreshToken") + ).thenReturn(request) + val newDate = Date(CredentialsMock.ONE_HOUR_AHEAD_MS + ONE_HOUR_SECONDS * 1000) + val jwtMock = mock() + Mockito.`when`(jwtMock.expiresAt).thenReturn(newDate) + Mockito.`when`(jwtDecoder.decode("newId")).thenReturn(jwtMock) + + // Trigger success + val newRefresh: String? = null + val renewedCredentials = + Credentials("newId", "newAccess", "newType", newRefresh, newDate, "newScope") + Mockito.`when`(request.execute()).thenReturn(renewedCredentials) + manager.getCredentials(null, 0, callback) + verify(callback).onSuccess( + credentialsCaptor.capture() + ) + verify(request) + .addParameter(eq("scope"), eq("saved scope")) + + // Verify the credentials are property stored + verify(storage).store("com.auth0.id_token", renewedCredentials.idToken) + verify(storage).store("com.auth0.access_token", renewedCredentials.accessToken) + // RefreshToken should not be replaced + verify(storage, never()).store("com.auth0.refresh_token", newRefresh) + verify(storage).store("com.auth0.refresh_token", "refreshToken") + verify(storage).store("com.auth0.token_type", renewedCredentials.type) + verify(storage).store( + "com.auth0.expires_at", renewedCredentials.expiresAt.time + ) + verify(storage).store("com.auth0.scope", renewedCredentials.scope) + verify(storage).store( + "com.auth0.cache_expires_at", renewedCredentials.expiresAt.time + ) + verify(storage, never()).remove(ArgumentMatchers.anyString()) + + // Verify the returned credentials are the latest + val retrievedCredentials = credentialsCaptor.firstValue + MatcherAssert.assertThat(retrievedCredentials, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(retrievedCredentials.idToken, Is.`is`("newId")) + MatcherAssert.assertThat(retrievedCredentials.accessToken, Is.`is`("newAccess")) + MatcherAssert.assertThat(retrievedCredentials.type, Is.`is`("newType")) + MatcherAssert.assertThat(retrievedCredentials.refreshToken, Is.`is`("refreshToken")) + MatcherAssert.assertThat(retrievedCredentials.expiresAt, Is.`is`(newDate)) + MatcherAssert.assertThat(retrievedCredentials.scope, Is.`is`("newScope")) + } + + @Test + public fun shouldRenewExpiredCredentialsIfSavedScopeIsNullAndRequiredScopeIsNotNull() { + Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken") + Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken") + Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") + Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type") + val expirationTime = CredentialsMock.CURRENT_TIME_MS // expired credentials + Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime) + Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at")) + .thenReturn(expirationTime) + Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn(null) + Mockito.`when`( + client.renewAuth("refreshToken") + ).thenReturn(request) + val newDate = Date(CredentialsMock.ONE_HOUR_AHEAD_MS + ONE_HOUR_SECONDS * 1000) + val jwtMock = mock() + Mockito.`when`(jwtMock.expiresAt).thenReturn(newDate) + Mockito.`when`(jwtDecoder.decode("newId")).thenReturn(jwtMock) + + // Trigger success + val newRefresh: String? = null + val renewedCredentials = + Credentials("newId", "newAccess", "newType", newRefresh, newDate, "newScope") + Mockito.`when`(request.execute()).thenReturn(renewedCredentials) + manager.getCredentials("required scope", 0, callback) + verify(callback).onSuccess( + credentialsCaptor.capture() + ) + verify(request) + .addParameter(eq("scope"), eq("required scope")) + + // Verify the credentials are property stored + verify(storage).store("com.auth0.id_token", renewedCredentials.idToken) + verify(storage).store("com.auth0.access_token", renewedCredentials.accessToken) + // RefreshToken should not be replaced + verify(storage, never()).store("com.auth0.refresh_token", newRefresh) + verify(storage).store("com.auth0.refresh_token", "refreshToken") + verify(storage).store("com.auth0.token_type", renewedCredentials.type) + verify(storage).store( + "com.auth0.expires_at", renewedCredentials.expiresAt.time + ) + verify(storage).store("com.auth0.scope", renewedCredentials.scope) + verify(storage).store( + "com.auth0.cache_expires_at", renewedCredentials.expiresAt.time + ) + verify(storage, never()).remove(ArgumentMatchers.anyString()) + + // Verify the returned credentials are the latest + val retrievedCredentials = credentialsCaptor.firstValue + MatcherAssert.assertThat(retrievedCredentials, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(retrievedCredentials.idToken, Is.`is`("newId")) + MatcherAssert.assertThat(retrievedCredentials.accessToken, Is.`is`("newAccess")) + MatcherAssert.assertThat(retrievedCredentials.type, Is.`is`("newType")) + MatcherAssert.assertThat(retrievedCredentials.refreshToken, Is.`is`("refreshToken")) + MatcherAssert.assertThat(retrievedCredentials.expiresAt, Is.`is`(newDate)) + MatcherAssert.assertThat(retrievedCredentials.scope, Is.`is`("newScope")) + } + + @Test + public fun shouldRenewExpiredCredentialsWhenScopesAreNull() { + Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken") + Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken") + Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") + Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type") + val expirationTime = CredentialsMock.CURRENT_TIME_MS // expired credentials + Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime) + Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at")) + .thenReturn(expirationTime) + Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn(null) + Mockito.`when`( + client.renewAuth("refreshToken") + ).thenReturn(request) + val newDate = Date(CredentialsMock.ONE_HOUR_AHEAD_MS + ONE_HOUR_SECONDS * 1000) + val jwtMock = mock() + Mockito.`when`(jwtMock.expiresAt).thenReturn(newDate) + Mockito.`when`(jwtDecoder.decode("newId")).thenReturn(jwtMock) + + // Trigger success + val newRefresh: String? = null + val renewedCredentials = + Credentials("newId", "newAccess", "newType", newRefresh, newDate, "newScope") + Mockito.`when`(request.execute()).thenReturn(renewedCredentials) + manager.getCredentials(null, 0, callback) + verify(callback).onSuccess( + credentialsCaptor.capture() + ) + verify(request, never()) + .addParameter(eq("scope"), ArgumentMatchers.anyString()) + + // Verify the credentials are property stored + verify(storage).store("com.auth0.id_token", renewedCredentials.idToken) + verify(storage).store("com.auth0.access_token", renewedCredentials.accessToken) + // RefreshToken should not be replaced + verify(storage, never()).store("com.auth0.refresh_token", newRefresh) + verify(storage).store("com.auth0.refresh_token", "refreshToken") + verify(storage).store("com.auth0.token_type", renewedCredentials.type) + verify(storage).store( + "com.auth0.expires_at", renewedCredentials.expiresAt.time + ) + verify(storage).store("com.auth0.scope", renewedCredentials.scope) + verify(storage).store( + "com.auth0.cache_expires_at", renewedCredentials.expiresAt.time + ) + verify(storage, never()).remove(ArgumentMatchers.anyString()) + + // Verify the returned credentials are the latest + val retrievedCredentials = credentialsCaptor.firstValue + MatcherAssert.assertThat(retrievedCredentials, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(retrievedCredentials.idToken, Is.`is`("newId")) + MatcherAssert.assertThat(retrievedCredentials.accessToken, Is.`is`("newAccess")) + MatcherAssert.assertThat(retrievedCredentials.type, Is.`is`("newType")) + MatcherAssert.assertThat(retrievedCredentials.refreshToken, Is.`is`("refreshToken")) + MatcherAssert.assertThat(retrievedCredentials.expiresAt, Is.`is`(newDate)) + MatcherAssert.assertThat(retrievedCredentials.scope, Is.`is`("newScope")) + } + @Test public fun shouldRenewCredentialsWithMinTtl() { Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken") @@ -642,8 +816,8 @@ public class CredentialsManagerTest { verify(callback).onSuccess( credentialsCaptor.capture() ) - verify(request, never()) - .addParameter(eq("scope"), ArgumentMatchers.anyString()) + verify(request) + .addParameter(eq("scope"), eq("scope")) // Verify the credentials are property stored verify(storage).store("com.auth0.id_token", renewedCredentials.idToken)