0

I am building an app that requires oauth 2 authentication. The endpoint to retrieve the access_token is https://10.0.2.2:8443/oauth/token (10.0.2.2 is loopback to get to localhost on host computer)

When I do the request through my browser it works just fine, but when I do it through Java code, I get a bad request, and I don't get enough info to troubleshoot.

Post request with PostMan

I use an unsafe HttpClient (Yes I know, this is terribly unsafe)

public class UnsafeHttpsClient {

    public static HttpClient getNewHttpClient() {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);

            MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(sf.ALLOW_ALL_HOSTNAME_VERIFIER);

            HttpParams params = new BasicHttpParams();
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 8080));
            registry.register(new Scheme("https", sf, 8443));

            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

            return new DefaultHttpClient(ccm, params);
        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }

}

I also use this class. The code was taken from this post on SO: Reference: Trusting all certificates using HttpClient over HTTPS

public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

And finally, I use both these classes to build my request:

public class TaskAuthenticate extends AsyncTask<String, Integer, JSONArray> {

    private Context ctx;
    public IApiAccessResponse delegate=null;
    private HttpClient mHttpclient = UnsafeHttpsClient.getNewHttpClient();
    private HttpPost mHttppost;
    private String client_string = "mobile:";
    public TaskAuthenticate (Context ctx) {
        this.ctx = ctx;
    }


    protected JSONArray doInBackground(String... params) {
         String strTokenUrl = ctx.getResources().getString(R.string.oauth2_endpoint);

         mHttppost = new HttpPost();
         try {
             mHttppost.setURI(new URI(strTokenUrl));
         }
         catch (URISyntaxException e1) {
             e1.printStackTrace();
         }
         List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
         nameValuePairs.add(new BasicNameValuePair("username", params[0]));
         nameValuePairs.add(new BasicNameValuePair("password", params[1]));
         nameValuePairs.add(new BasicNameValuePair("grant_type", "password"));
         nameValuePairs.add(new BasicNameValuePair("client_id", "mobile"));
//       nameValuePairs.add(new BasicNameValuePair("client_secret",));
         try {
            String header = "Basic " + Base64.encodeToString(client_string.getBytes("UTF-8"), Base64.DEFAULT);
            mHttppost.setHeader("Authorization", header);
            mHttppost.setHeader("Content-Type",
                    "application/x-www-form-urlencoded;charset=UTF-8");
            mHttppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
         }
         catch (UnsupportedEncodingException e) {
            e.printStackTrace();
         }

        HttpResponse response;
        try {
            response  = mHttpclient.execute(mHttppost);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            response = null;
        } catch (IOException e) {
            e.printStackTrace();
            response = null;
        }

        JSONArray result = null;
        try {
            result = new JSONArray(EntityUtils.toString(response.getEntity()));
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

When I execute this, I get a 400 - Bad request response. Also if I try to grab the response body with the following code

 HttpEntity respEntity = response.getEntity();
    if(respEntity!=null) {
        String res = EntityUtils.toString(respEntity);
    }

The body comes back as an empty string So far, I was unable to reproduce in my browser so I am kind of clueless about what the problem might be. Is there something I am fundamentally doing wrong ? Any tips to debug this would be appreciated

If the server code is required, I will post it, but I think the problem is in the app since I can do the request in the browser.

Community
  • 1
  • 1
QuantumLicht
  • 2,103
  • 3
  • 23
  • 32

1 Answers1

0

I figured out what was the problem.It appears when you add a header for your request, end-of-line character only cause problems for POST request. The fix was to change the bitmask to make sure the end of line character is not present. I replaced this line:

String header = "Basic " + Base64.encodeToString(client_string.getBytes("UTF-8"), Base64.DEFAULT);

by:

String header = "Basic " + Base64.encodeToString(client_string.getBytes("UTF-8"), Base64.URL_SAFE|Base64.NO_WRAP);
QuantumLicht
  • 2,103
  • 3
  • 23
  • 32