diff --git a/build.gradle b/build.gradle index f3002cbe..69027066 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ if (project.ext.versionName == null) { project.ext.minSdkVersion = 15 project.ext.compileSdkVersion = 25 -project.ext.buildToolsVersion = '25.0.1' +project.ext.buildToolsVersion = '25.0.2' project.ext.supportLibVersion = '25.1.0' task showVersion { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7ddb5c9e..b8d39c83 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jan 18 10:13:16 PST 2017 +#Tue Jan 24 13:57:21 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/library/java/net/openid/appauth/AuthorizationService.java b/library/java/net/openid/appauth/AuthorizationService.java index 50e20f26..10c00204 100644 --- a/library/java/net/openid/appauth/AuthorizationService.java +++ b/library/java/net/openid/appauth/AuthorizationService.java @@ -36,8 +36,10 @@ import org.json.JSONException; import org.json.JSONObject; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.util.Map; @@ -296,6 +298,39 @@ private void checkNotDisposed() { } } + private AuthorizationException exceptionForErrorStream(InputStream is, int errorType) + throws IOException { + + BufferedReader inputReader = new BufferedReader(new InputStreamReader(is)); + StringBuilder jsonStringBuilder = new StringBuilder(); + String currentlyReadLine; + + try { + while (( currentlyReadLine = inputReader.readLine() ) != null) { + + jsonStringBuilder.append(currentlyReadLine); + } + + JSONObject jsonResult = new JSONObject(jsonStringBuilder.toString()); + String errorString = jsonResult.getString("error"); + switch (errorType) { + + case AuthorizationException.TYPE_OAUTH_TOKEN_ERROR: + return AuthorizationException.TokenRequestErrors.byString(errorString); + + default: + case AuthorizationException.TYPE_OAUTH_REGISTRATION_ERROR: + return AuthorizationException.RegistrationRequestErrors.byString(errorString); + } + } + catch (JSONException jsonExc) { + + return AuthorizationException + .fromTemplate(GeneralErrors.JSON_DESERIALIZATION_ERROR, jsonExc); + } + } + + private class TokenRequestTask extends AsyncTask { private TokenRequest mRequest; @@ -344,14 +379,26 @@ protected JSONObject doInBackground(Void... voids) { wr.write(queryData); wr.flush(); - if (conn.getResponseCode() >= HttpURLConnection.HTTP_OK - && conn.getResponseCode() < HttpURLConnection.HTTP_MULT_CHOICE) { + try { is = conn.getInputStream(); - } else { + String response = Utils.readInputStream(is); + return new JSONObject(response); + } + catch (IOException isIoExc) { + is = conn.getErrorStream(); + + if (is != null) { + + mException = exceptionForErrorStream(is, + AuthorizationException.TYPE_OAUTH_TOKEN_ERROR); + } + else { + + mException = AuthorizationException.fromTemplate( + GeneralErrors.NETWORK_ERROR, isIoExc); + } } - String response = Utils.readInputStream(is); - return new JSONObject(response); } catch (IOException ex) { Logger.debugWithStack(ex, "Failed to complete exchange request"); mException = AuthorizationException.fromTemplate( @@ -459,9 +506,26 @@ protected JSONObject doInBackground(Void... voids) { wr.write(postData); wr.flush(); - is = conn.getInputStream(); - String response = Utils.readInputStream(is); - return new JSONObject(response); + try { + is = conn.getInputStream(); + String response = Utils.readInputStream(is); + return new JSONObject(response); + } + catch (IOException isIoExc) { + + is = conn.getErrorStream(); + + if (is != null) { + + mException = exceptionForErrorStream(is, + AuthorizationException.TYPE_OAUTH_REGISTRATION_ERROR); + } + else { + + mException = AuthorizationException.fromTemplate( + GeneralErrors.NETWORK_ERROR, isIoExc); + } + } } catch (IOException ex) { Logger.debugWithStack(ex, "Failed to complete registration request"); mException = AuthorizationException.fromTemplate( diff --git a/library/javatests/net/openid/appauth/AuthorizationServiceTest.java b/library/javatests/net/openid/appauth/AuthorizationServiceTest.java index e972efc4..6019da28 100644 --- a/library/javatests/net/openid/appauth/AuthorizationServiceTest.java +++ b/library/javatests/net/openid/appauth/AuthorizationServiceTest.java @@ -48,6 +48,7 @@ import android.support.customtabs.CustomTabsServiceConnection; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -55,6 +56,7 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -185,6 +187,17 @@ public void testTokenRequest() throws Exception { assertThat(postBody).isEqualTo(UriUtil.formUrlEncode(request.getRequestParameters())); } + @Test + public void testTokenRequest_invalidJson() throws Exception { + InputStream is = new ByteArrayInputStream("{\"invalid_json".getBytes()); + when(mHttpConnection.getInputStream()).thenReturn(is); + TokenRequest request = getTestAuthCodeExchangeRequest(); + mService.performTokenRequest(request, mAuthCallback); + mAuthCallback.waitForCallback(); + assertNotNull(mAuthCallback.error); + assertEquals(GeneralErrors.JSON_DESERIALIZATION_ERROR, mAuthCallback.error); + } + @Test public void testTokenRequest_withBasicAuth() throws Exception { ClientSecretBasic csb = new ClientSecretBasic(TEST_CLIENT_SECRET); @@ -222,6 +235,7 @@ public void testTokenRequest_withPostAuth() throws Exception { public void testTokenRequest_withInvalidGrant() throws Exception { ClientSecretPost csp = new ClientSecretPost(TEST_CLIENT_SECRET); InputStream is = new ByteArrayInputStream(INVALID_GRANT_RESPONSE_JSON.getBytes()); + when(mHttpConnection.getInputStream()).thenThrow(new FileNotFoundException()); when(mHttpConnection.getErrorStream()).thenReturn(is); when(mHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_BAD_REQUEST); TokenRequest request = getTestAuthCodeExchangeRequest(); @@ -234,6 +248,7 @@ public void testTokenRequest_withInvalidGrant() throws Exception { public void testTokenRequest_withInvalidGrant2() throws Exception { ClientSecretPost csp = new ClientSecretPost(TEST_CLIENT_SECRET); InputStream is = new ByteArrayInputStream(INVALID_GRANT_RESPONSE_JSON.getBytes()); + when(mHttpConnection.getInputStream()).thenThrow(new FileNotFoundException()); when(mHttpConnection.getErrorStream()).thenReturn(is); when(mHttpConnection.getResponseCode()).thenReturn(199); TokenRequest request = getTestAuthCodeExchangeRequest(); @@ -253,6 +268,48 @@ public void testTokenRequest_IoException() throws Exception { assertEquals(GeneralErrors.NETWORK_ERROR, mAuthCallback.error); } + @Test + public void testTokenRequest_400StatusCode() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getInputStream()).thenThrow(ex); + final String json = "{\"error\":\"invalid_grant\"}"; + final InputStream errorJsonInputStream = new ByteArrayInputStream(json.getBytes("UTF-8")); + when(mHttpConnection.getErrorStream()).thenReturn(errorJsonInputStream); + + mService.performTokenRequest(getTestAuthCodeExchangeRequest(), mAuthCallback); + mAuthCallback.waitForCallback(); + assertNotNull(mAuthCallback.error); + assertEquals(AuthorizationException.TokenRequestErrors.INVALID_GRANT, mAuthCallback.error); + } + + @Test + public void testTokenRequest_400InvalidJsonBody() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getInputStream()).thenThrow(ex); + final String json = "{\"error\":\"invalid_grant"; + final InputStream errorJsonInputStream = new ByteArrayInputStream(json.getBytes("UTF-8")); + when(mHttpConnection.getErrorStream()).thenReturn(errorJsonInputStream); + + mService.performTokenRequest(getTestAuthCodeExchangeRequest(), mAuthCallback); + mAuthCallback.waitForCallback(); + assertNotNull(mAuthCallback.error); + assertEquals(GeneralErrors.JSON_DESERIALIZATION_ERROR, mAuthCallback.error); + } + + @Test + public void testTokenRequest_connectionOutputStreamException() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getOutputStream()).thenThrow(ex); + + mService.performTokenRequest(getTestAuthCodeExchangeRequest(), mAuthCallback); + mAuthCallback.waitForCallback(); + assertNotNull(mAuthCallback.error); + assertEquals(GeneralErrors.NETWORK_ERROR, mAuthCallback.error); + } + @Test public void testRegistrationRequest() throws Exception { InputStream is = new ByteArrayInputStream(REGISTRATION_RESPONSE_JSON.getBytes()); @@ -265,6 +322,17 @@ public void testRegistrationRequest() throws Exception { assertThat(postBody).isEqualTo(request.toJsonString()); } + @Test + public void testRegistrationRequest_invalidJson() throws Exception { + InputStream is = new ByteArrayInputStream("{\"invalid_json".getBytes()); + when(mHttpConnection.getInputStream()).thenReturn(is); + RegistrationRequest request = getTestRegistrationRequest(); + mService.performRegistrationRequest(request, mRegistrationCallback); + mRegistrationCallback.waitForCallback(); + assertNotNull(mRegistrationCallback.error); + assertEquals(GeneralErrors.JSON_DESERIALIZATION_ERROR, mRegistrationCallback.error); + } + @Test public void testRegistrationRequest_IoException() throws Exception { Exception ex = new IOException(); @@ -275,6 +343,48 @@ public void testRegistrationRequest_IoException() throws Exception { assertEquals(GeneralErrors.NETWORK_ERROR, mRegistrationCallback.error); } + @Test + public void testRegistrationRequest_400StatusCode() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getInputStream()).thenThrow(ex); + final String json = "{\"error\":\"invalid_request\"}"; + final InputStream errorJsonInputStream = new ByteArrayInputStream(json.getBytes("UTF-8")); + when(mHttpConnection.getErrorStream()).thenReturn(errorJsonInputStream); + + mService.performRegistrationRequest(getTestRegistrationRequest(), mRegistrationCallback); + mRegistrationCallback.waitForCallback(); + assertNotNull(mRegistrationCallback.error); + assertEquals(AuthorizationException.RegistrationRequestErrors.INVALID_REQUEST, + mRegistrationCallback.error); + } + + @Test + public void testRegistrationRequest_400InvalidJson() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getInputStream()).thenThrow(ex); + final String json = "{\"error\":\"invalid_request"; + final InputStream errorJsonInputStream = new ByteArrayInputStream(json.getBytes("UTF-8")); + when(mHttpConnection.getErrorStream()).thenReturn(errorJsonInputStream); + + mService.performRegistrationRequest(getTestRegistrationRequest(), mRegistrationCallback); + mRegistrationCallback.waitForCallback(); + assertNotNull(mRegistrationCallback.error); + assertEquals(GeneralErrors.JSON_DESERIALIZATION_ERROR, mRegistrationCallback.error); + } + + @Test + public void testRegistrationRequest_outputStreamException() throws Exception { + + Exception ex = new IOException(); + when(mHttpConnection.getOutputStream()).thenThrow(ex); + + mService.performRegistrationRequest(getTestRegistrationRequest(), mRegistrationCallback); + mRegistrationCallback.waitForCallback(); + assertNotNull(mRegistrationCallback.error); + assertEquals(GeneralErrors.NETWORK_ERROR, mRegistrationCallback.error); + } @Test(expected = IllegalStateException.class) public void testTokenRequest_afterDispose() throws Exception { mService.dispose(); @@ -311,7 +421,6 @@ private void assertInvalidGrant(AuthorizationException error) { assertEquals(AuthorizationException.TYPE_OAUTH_TOKEN_ERROR, error.type); assertEquals(TEST_INVALID_GRANT_CODE, error.code); assertEquals("invalid_grant", error.error); - assertEquals("invalid_grant description", error.errorDescription); } private void assertRegistrationResponse(RegistrationResponse response,