Skip to content

Commit 1676b16

Browse files
authored
Added new error types for CredentialsManagerException (#783)
2 parents d5d2597 + 0c6d236 commit 1676b16

File tree

6 files changed

+275
-10
lines changed

6 files changed

+275
-10
lines changed

auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,15 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
325325
saveCredentials(credentials)
326326
callback.onSuccess(credentials)
327327
} catch (error: AuthenticationException) {
328+
val exception = when {
329+
error.isRefreshTokenDeleted ||
330+
error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED
331+
error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK
332+
else -> CredentialsManagerException.Code.API_ERROR
333+
}
328334
callback.onFailure(
329335
CredentialsManagerException(
330-
CredentialsManagerException.Code.RENEW_FAILED,
336+
exception,
331337
error
332338
)
333339
)

auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class CredentialsManagerException :
4343
BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
4444
BIOMETRICS_INVALID_USER,
4545
BIOMETRIC_AUTHENTICATION_FAILED,
46+
NO_NETWORK,
47+
API_ERROR
4648
}
4749

4850
private var code: Code?
@@ -135,6 +137,12 @@ public class CredentialsManagerException :
135137
public val BIOMETRICS_INVALID_USER: CredentialsManagerException =
136138
CredentialsManagerException(Code.BIOMETRICS_INVALID_USER)
137139

140+
//Exceptions thrown when making api calls for access token renewal
141+
public val NO_NETWORK: CredentialsManagerException =
142+
CredentialsManagerException(Code.NO_NETWORK)
143+
public val API_ERROR: CredentialsManagerException =
144+
CredentialsManagerException(Code.API_ERROR)
145+
138146

139147
private fun getMessage(code: Code): String {
140148
return when (code) {
@@ -177,6 +185,8 @@ public class CredentialsManagerException :
177185
Code.BIOMETRIC_ERROR_UNABLE_TO_PROCESS -> "Failed to authenticate because the sensor was unable to process the current image."
178186
Code.BIOMETRICS_INVALID_USER -> "The user didn't pass the authentication challenge."
179187
Code.BIOMETRIC_AUTHENTICATION_FAILED -> "Biometric authentication failed."
188+
Code.NO_NETWORK -> "Failed to execute the network request."
189+
Code.API_ERROR -> "An error occurred while processing the request."
180190
}
181191
}
182192
}

auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import android.util.Log
77
import androidx.annotation.VisibleForTesting
88
import androidx.fragment.app.FragmentActivity
99
import com.auth0.android.Auth0
10-
import com.auth0.android.Auth0Exception
1110
import com.auth0.android.authentication.AuthenticationAPIClient
11+
import com.auth0.android.authentication.AuthenticationException
1212
import com.auth0.android.callback.Callback
1313
import com.auth0.android.request.internal.GsonProvider
1414
import com.auth0.android.result.Credentials
@@ -585,10 +585,16 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
585585
fresh.expiresAt,
586586
fresh.scope
587587
)
588-
} catch (error: Auth0Exception) {
588+
} catch (error: AuthenticationException) {
589+
val exception = when {
590+
error.isRefreshTokenDeleted ||
591+
error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED
592+
error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK
593+
else -> CredentialsManagerException.Code.API_ERROR
594+
}
589595
callback.onFailure(
590596
CredentialsManagerException(
591-
CredentialsManagerException.Code.RENEW_FAILED,
597+
exception,
592598
error
593599
)
594600
)

auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.auth0.android.authentication.storage
22

3+
import com.auth0.android.NetworkErrorException
34
import com.auth0.android.authentication.AuthenticationAPIClient
45
import com.auth0.android.authentication.AuthenticationException
56
import com.auth0.android.callback.Callback
@@ -788,7 +789,7 @@ public class CredentialsManagerTest {
788789
}
789790

790791
@Test
791-
public fun shouldGetAndFailToRenewExpiredCredentials() {
792+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenRefreshTokenExpired() {
792793
Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken")
793794
Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken")
794795
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
@@ -802,8 +803,9 @@ public class CredentialsManagerTest {
802803
client.renewAuth("refreshToken")
803804
).thenReturn(request)
804805
//Trigger failure
805-
val authenticationException = Mockito.mock(
806-
AuthenticationException::class.java
806+
val authenticationException = AuthenticationException(
807+
"invalid_grant",
808+
"Unknown or invalid refresh token."
807809
)
808810
Mockito.`when`(request.execute()).thenThrow(authenticationException)
809811
manager.getCredentials(callback)
@@ -828,6 +830,130 @@ public class CredentialsManagerTest {
828830
)
829831
}
830832

833+
@Test
834+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenUserIsDeleted() {
835+
Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken")
836+
Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken")
837+
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
838+
Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type")
839+
val expirationTime = CredentialsMock.CURRENT_TIME_MS //Same as current time --> expired
840+
Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime)
841+
Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at"))
842+
.thenReturn(expirationTime)
843+
Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn("scope")
844+
Mockito.`when`(
845+
client.renewAuth("refreshToken")
846+
).thenReturn(request)
847+
//Trigger failure
848+
val authenticationException = AuthenticationException(
849+
mapOf(
850+
"error" to "invalid_grant",
851+
"error_description" to "The refresh_token was generated for a user who doesn't exist anymore."
852+
), 403
853+
)
854+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
855+
manager.getCredentials(callback)
856+
verify(storage, never())
857+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt())
858+
verify(storage, never())
859+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong())
860+
verify(storage, never())
861+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
862+
verify(storage, never())
863+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())
864+
verify(storage, never()).remove(ArgumentMatchers.anyString())
865+
verify(callback).onFailure(
866+
exceptionCaptor.capture()
867+
)
868+
val exception = exceptionCaptor.firstValue
869+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
870+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
871+
MatcherAssert.assertThat(
872+
exception.message,
873+
Is.`is`("An error occurred while trying to use the Refresh Token to renew the Credentials.")
874+
)
875+
}
876+
877+
@Test
878+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenNetworkIsNotAvailable() {
879+
Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken")
880+
Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken")
881+
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
882+
Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type")
883+
val expirationTime = CredentialsMock.CURRENT_TIME_MS //Same as current time --> expired
884+
Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime)
885+
Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at"))
886+
.thenReturn(expirationTime)
887+
Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn("scope")
888+
Mockito.`when`(
889+
client.renewAuth("refreshToken")
890+
).thenReturn(request)
891+
//Trigger failure
892+
val authenticationException = AuthenticationException(
893+
"Failed to execute the network request", NetworkErrorException(mock())
894+
)
895+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
896+
manager.getCredentials(callback)
897+
verify(storage, never())
898+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt())
899+
verify(storage, never())
900+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong())
901+
verify(storage, never())
902+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
903+
verify(storage, never())
904+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())
905+
verify(storage, never()).remove(ArgumentMatchers.anyString())
906+
verify(callback).onFailure(
907+
exceptionCaptor.capture()
908+
)
909+
val exception = exceptionCaptor.firstValue
910+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
911+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
912+
MatcherAssert.assertThat(
913+
exception.message,
914+
Is.`is`("Failed to execute the network request.")
915+
)
916+
}
917+
918+
@Test
919+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenApiErrorOccurs() {
920+
Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken")
921+
Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken")
922+
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
923+
Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type")
924+
val expirationTime = CredentialsMock.CURRENT_TIME_MS //Same as current time --> expired
925+
Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime)
926+
Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at"))
927+
.thenReturn(expirationTime)
928+
Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn("scope")
929+
Mockito.`when`(
930+
client.renewAuth("refreshToken")
931+
).thenReturn(request)
932+
//Trigger failure
933+
val authenticationException = AuthenticationException("Something went wrong", mock<Exception>())
934+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
935+
manager.getCredentials(callback)
936+
verify(storage, never())
937+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt())
938+
verify(storage, never())
939+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong())
940+
verify(storage, never())
941+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
942+
verify(storage, never())
943+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())
944+
verify(storage, never()).remove(ArgumentMatchers.anyString())
945+
verify(callback).onFailure(
946+
exceptionCaptor.capture()
947+
)
948+
val exception = exceptionCaptor.firstValue
949+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
950+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
951+
MatcherAssert.assertThat(
952+
exception.message,
953+
Is.`is`("An error occurred while processing the request.")
954+
)
955+
}
956+
831957
@Test
832958
public fun shouldClearCredentials() {
833959
manager.clearCredentials()

auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.content.Context
66
import android.util.Base64
77
import androidx.fragment.app.FragmentActivity
88
import com.auth0.android.Auth0
9+
import com.auth0.android.NetworkErrorException
910
import com.auth0.android.authentication.AuthenticationAPIClient
1011
import com.auth0.android.authentication.AuthenticationException
1112
import com.auth0.android.callback.Callback
@@ -1259,7 +1260,7 @@ public class SecureCredentialsManagerTest {
12591260
}
12601261

12611262
@Test
1262-
public fun shouldGetAndFailToRenewExpiredCredentials() {
1263+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenRefreshTokenExpired() {
12631264
Mockito.`when`(localAuthenticationManager.authenticate()).then {
12641265
localAuthenticationManager.resultCallback.onSuccess(true)
12651266
}
@@ -1269,7 +1270,50 @@ public class SecureCredentialsManagerTest {
12691270
client.renewAuth("refreshToken")
12701271
).thenReturn(request)
12711272
//Trigger failure
1272-
val authenticationException = mock<AuthenticationException>()
1273+
val authenticationException = AuthenticationException(
1274+
"invalid_grant",
1275+
"Unknown or invalid refresh token."
1276+
)
1277+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
1278+
manager.getCredentials(callback)
1279+
verify(callback).onFailure(
1280+
exceptionCaptor.capture()
1281+
)
1282+
verify(storage, never())
1283+
.store(anyString(), anyLong())
1284+
verify(storage, never())
1285+
.store(anyString(), anyInt())
1286+
verify(storage, never())
1287+
.store(anyString(), anyString())
1288+
verify(storage, never())
1289+
.store(anyString(), anyBoolean())
1290+
verify(storage, never()).remove(anyString())
1291+
val exception = exceptionCaptor.firstValue
1292+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
1293+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
1294+
MatcherAssert.assertThat(
1295+
exception.message,
1296+
Is.`is`("An error occurred while trying to use the Refresh Token to renew the Credentials.")
1297+
)
1298+
}
1299+
1300+
@Test
1301+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenUserIsDeleted() {
1302+
Mockito.`when`(localAuthenticationManager.authenticate()).then {
1303+
localAuthenticationManager.resultCallback.onSuccess(true)
1304+
}
1305+
val expiresAt = Date(CredentialsMock.CURRENT_TIME_MS)
1306+
insertTestCredentials(false, true, true, expiresAt, "scope")
1307+
Mockito.`when`(
1308+
client.renewAuth("refreshToken")
1309+
).thenReturn(request)
1310+
//Trigger failure
1311+
val authenticationException = AuthenticationException(
1312+
mapOf(
1313+
"error" to "invalid_grant",
1314+
"error_description" to "The refresh_token was generated for a user who doesn't exist anymore."
1315+
), 403
1316+
)
12731317
Mockito.`when`(request.execute()).thenThrow(authenticationException)
12741318
manager.getCredentials(callback)
12751319
verify(callback).onFailure(
@@ -1293,6 +1337,79 @@ public class SecureCredentialsManagerTest {
12931337
)
12941338
}
12951339

1340+
@Test
1341+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenNetworkIsNotAvailable() {
1342+
Mockito.`when`(localAuthenticationManager.authenticate()).then {
1343+
localAuthenticationManager.resultCallback.onSuccess(true)
1344+
}
1345+
val expiresAt = Date(CredentialsMock.CURRENT_TIME_MS)
1346+
insertTestCredentials(false, true, true, expiresAt, "scope")
1347+
Mockito.`when`(
1348+
client.renewAuth("refreshToken")
1349+
).thenReturn(request)
1350+
//Trigger failure
1351+
val authenticationException = AuthenticationException(
1352+
"Failed to execute the network request", NetworkErrorException(mock())
1353+
)
1354+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
1355+
manager.getCredentials(callback)
1356+
verify(callback).onFailure(
1357+
exceptionCaptor.capture()
1358+
)
1359+
verify(storage, never())
1360+
.store(anyString(), anyLong())
1361+
verify(storage, never())
1362+
.store(anyString(), anyInt())
1363+
verify(storage, never())
1364+
.store(anyString(), anyString())
1365+
verify(storage, never())
1366+
.store(anyString(), anyBoolean())
1367+
verify(storage, never()).remove(anyString())
1368+
val exception = exceptionCaptor.firstValue
1369+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
1370+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
1371+
MatcherAssert.assertThat(
1372+
exception.message,
1373+
Is.`is`("Failed to execute the network request.")
1374+
)
1375+
}
1376+
1377+
@Test
1378+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenApiErrorOccurs() {
1379+
Mockito.`when`(localAuthenticationManager.authenticate()).then {
1380+
localAuthenticationManager.resultCallback.onSuccess(true)
1381+
}
1382+
val expiresAt = Date(CredentialsMock.CURRENT_TIME_MS)
1383+
insertTestCredentials(false, true, true, expiresAt, "scope")
1384+
Mockito.`when`(
1385+
client.renewAuth("refreshToken")
1386+
).thenReturn(request)
1387+
//Trigger failure
1388+
val authenticationException =
1389+
AuthenticationException("Something went wrong", mock<Exception>())
1390+
Mockito.`when`(request.execute()).thenThrow(authenticationException)
1391+
manager.getCredentials(callback)
1392+
verify(callback).onFailure(
1393+
exceptionCaptor.capture()
1394+
)
1395+
verify(storage, never())
1396+
.store(anyString(), anyLong())
1397+
verify(storage, never())
1398+
.store(anyString(), anyInt())
1399+
verify(storage, never())
1400+
.store(anyString(), anyString())
1401+
verify(storage, never())
1402+
.store(anyString(), anyBoolean())
1403+
verify(storage, never()).remove(anyString())
1404+
val exception = exceptionCaptor.firstValue
1405+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
1406+
MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException))
1407+
MatcherAssert.assertThat(
1408+
exception.message,
1409+
Is.`is`("An error occurred while processing the request.")
1410+
)
1411+
}
1412+
12961413
/**
12971414
* Testing that getCredentials execution from multiple threads via multiple instances of SecureCredentialsManager should trigger only one network request.
12981415
*/
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<resources>
22
<string name="app_name">Auth0 SDK Sample</string>
3-
<string name="com_auth0_domain">mathewp.acmetest.org</string>
3+
<string name="com_auth0_domain">p-mathew.us.auth0.com</string>
44
<string name="com_auth0_client_id">gkba7X6OJM2b0cdlUlTCqXD7AwT3FYVV</string>
55
<string name="com_auth0_scheme">demo</string>
66
</resources>

0 commit comments

Comments
 (0)