diff --git a/forgerock-auth/src/main/java/org/forgerock/android/auth/SessionManager.java b/forgerock-auth/src/main/java/org/forgerock/android/auth/SessionManager.java index dcc3c046..abb23d97 100644 --- a/forgerock-auth/src/main/java/org/forgerock/android/auth/SessionManager.java +++ b/forgerock-auth/src/main/java/org/forgerock/android/auth/SessionManager.java @@ -52,17 +52,12 @@ public SessionManager(TokenManager tokenManager, SingleSignOnManager singleSignO */ @WorkerThread public void refresh(FRListener listener) { - getAccessToken(new FRListener<>() { - @Override - public void onSuccess(AccessToken result) { - tokenManager.refresh(result, listener); - } - - @Override - public void onException(@NonNull Exception e) { - Listener.onException(listener, e); - } - }); + AccessToken token = tokenManager.getAccessToken(); + if(token == null) { + Listener.onException(listener, new AuthenticationRequiredException("Access Token does not exists.")); + return; + } + tokenManager.refresh(token, listener); } /** diff --git a/forgerock-auth/src/test/java/org/forgerock/android/auth/FRUserMockTest.kt b/forgerock-auth/src/test/java/org/forgerock/android/auth/FRUserMockTest.kt index 9036cd44..8de816bf 100644 --- a/forgerock-auth/src/test/java/org/forgerock/android/auth/FRUserMockTest.kt +++ b/forgerock-auth/src/test/java/org/forgerock/android/auth/FRUserMockTest.kt @@ -304,6 +304,86 @@ class FRUserMockTest : BaseTest() { Assert.assertNotEquals(future.get().value, refreshTokenFuture.get().value) Assert.assertNotEquals(future.get().idToken, refreshTokenFuture.get().idToken) } + @Test(expected = AuthenticationRequiredException::class) + @Throws( + Throwable::class) + fun testAccessTokenIsNullThrowException() { + frUserHappyPath() + val future = FRListenerFuture() + FRUser.getCurrentUser().getAccessToken(future) + Assert.assertNotNull(future.get()) + // This will clear the token in the OIDC storage + Config.getInstance().oidcStorage.delete() + val refreshTokenFuture = FRListenerFuture() + FRUser.getCurrentUser().refresh(refreshTokenFuture) + // verify the exception + try { + refreshTokenFuture.get() + Assert.fail() + } catch (e: ExecutionException) { + Assert.assertTrue(e.message!!.contains("Access Token does not exists.")) + throw e.cause!! + } + } + + @Test(expected = AuthenticationRequiredException::class) + @Throws( + Throwable::class) + fun testRefreshTokenIsNullThrowException() { + enqueue("/authTreeMockTest_Authenticate_NameCallback.json", HttpURLConnection.HTTP_OK) + enqueue("/authTreeMockTest_Authenticate_PasswordCallback.json", HttpURLConnection.HTTP_OK) + enqueue("/authTreeMockTest_Authenticate_success.json", HttpURLConnection.HTTP_OK) + + Config.getInstance().oidcStorage = tokenStorage + Config.getInstance().ssoTokenStorage = ssoTokenStorage + Config.getInstance().cookiesStorage = cookiesStorage + Config.getInstance().url = url + + val nodeListenerFuture: NodeListenerFuture = + object : NodeListenerFuture() { + override fun onCallbackReceived(state: Node) { + if (state.getCallback(NameCallback::class.java) != null) { + state.getCallback(NameCallback::class.java).setName("tester") + state.next(context, this) + return + } + if (state.getCallback(PasswordCallback::class.java) != null) { + state.getCallback(PasswordCallback::class.java) + .setPassword("password".toCharArray()) + state.next(context, this) + } + } + } + + FRUser.login(context, nodeListenerFuture) + server.takeRequest() + server.takeRequest() + server.takeRequest() + val recordedRequest = server.takeRequest() + val state = Uri.parse(recordedRequest.path).getQueryParameter("state") + server.enqueue(MockResponse() + .addHeader("Location", + "http://www.example.com:8080/callback?code=PmxwECH3mBobKuPEtPmq6Xorgzo&iss=http://openam.example.com:8080/openam/oauth2&" + + "state=" + state + "&client_id=andy_app") + .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)) + // AccessToken returned from the server is empty. The storage does not have refresh token. + enqueue("/authTreeMockTest_Authenticate_no_accessToken_no_RefreshToken.json", HttpURLConnection.HTTP_OK) + + Assert.assertNotNull(nodeListenerFuture.get()) + Assert.assertNotNull(FRUser.getCurrentUser()) + + //Check RefreshToken State by sending future as a listener. + val refreshTokenFuture = FRListenerFuture() + FRUser.getCurrentUser().refresh(refreshTokenFuture) + // If the RefreshToken is null, the refresh token flow should throw an AuthenticationRequiredException. + try { + refreshTokenFuture.get() + Assert.fail() + } catch (e: ExecutionException) { + Assert.assertTrue(e.message!!.contains("Refresh Token does not exists.")) + throw e.cause!! + } + } @Test(expected = AuthenticationException::class) @Throws( diff --git a/forgerock-auth/src/test/resources/authTreeMockTest_Authenticate_no_accessToken_no_RefreshToken.json b/forgerock-auth/src/test/resources/authTreeMockTest_Authenticate_no_accessToken_no_RefreshToken.json new file mode 100644 index 00000000..1c028586 --- /dev/null +++ b/forgerock-auth/src/test/resources/authTreeMockTest_Authenticate_no_accessToken_no_RefreshToken.json @@ -0,0 +1,5 @@ +{ + "access_token": "", + "scope": "openid email address", + "expires_in": 3599 +} \ No newline at end of file diff --git a/samples/app/src/main/java/com/example/app/token/TokenViewModel.kt b/samples/app/src/main/java/com/example/app/token/TokenViewModel.kt index b9fe502d..7071a213 100644 --- a/samples/app/src/main/java/com/example/app/token/TokenViewModel.kt +++ b/samples/app/src/main/java/com/example/app/token/TokenViewModel.kt @@ -39,8 +39,7 @@ class TokenViewModel : ViewModel() { }) } - fun forceRefresh(token: AccessToken? = FRUser.getCurrentUser()?.accessToken) { - token?.let { + fun forceRefresh() { FRUser.getCurrentUser()?.refresh(object : FRListener { override fun onSuccess(result: AccessToken) { state.update { @@ -54,8 +53,7 @@ class TokenViewModel : ViewModel() { } } }) - } - } + } fun revoke(token: AccessToken? = FRUser.getCurrentUser()?.accessToken) { token?.let {