Search code examples
javaoauth-2.0x-www-form-urlencodedinvalid-request-uri

Oauth2 token call failing with "request is missing a required parameter"


I'm trying to connect to the REST API using application/x-www-form-urlencoded, using the following parameters:

 private String tokenEndpoint = "https://xxxxxxxxxxxxx/oauth2/token";
    private String client_id = "2xxxxxxxxxxxxxxxxx";
    private String client_secret = "xxxxxxxxxxxxxxxxxxxx";
    private String grant_type = "client_credentials";
    private String scope = "projects:read";


and finally get the following error:

{"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client credentials missing or malformed in both HTTP Authorization header and HTTP POST body."}

My code is:

package testRestAPI;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.net.ssl.SSLContext;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.TrustStrategy;


public class OAuthClient {

    private String tokenEndpoint = "https://xxxxxxxxxxxxx/oauth2/token";
    private String client_id = "2xxxxxxxxxxxxxxxxx";
    private String client_secret = "xxxxxxxxxxxxxxxxxxxx";
    private String grant_type = "client_credentials";
    private String scope = "projects:read";
    

    public String callService()  {


        String result = null;

        try {

                        //My site is set as insecure, so I set the following setting
            TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

            SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();

            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(csf)
                    .build();


            /* HTTPCLIENT AND HTTPPOST OOBJECT */
            //      HttpClient httpClient = HttpClientBuilder.create().build();
            HttpPost httpPost = new HttpPost(tokenEndpoint);

            /* AUTHENTICATION CREDENTIALS ENCODING */
            String base64Credentials = Base64.getEncoder().encodeToString((client_id + ":" 
                    + client_secret).getBytes());

            /* HEADER INFO */
            httpPost.addHeader("Authorization","Bearer " +  base64Credentials);
            httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");

            //      /* PROXY CONFIG */
            //              HttpHost target = new HttpHost("proxy", 8080, "http");
            //              RequestConfig config = RequestConfig.custom().setProxy(target).build();
            //              httpPost.setConfig(config);

            /* OAUTH PARAMETERS ADDED TO BODY */
            StringEntity input = null;
            try {
                input = new StringEntity("grant_type=" + grant_type + "&scope=" + scope);
                httpPost.setEntity(input);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            /* SEND AND RETRIEVE RESPONSE */
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            } catch (IOException e) {
                e.printStackTrace();
            }

            /* RESPONSE AS STRING */
            //      String result = null;
            try {
                result = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        catch (Exception e) {
            // TODO: handle exception
        }
        return result;
    }

    public static void main(String[] args) {
        OAuthClient oauthClient = new OAuthClient();
        String res = oauthClient.callService();
        System.out.println(res);
    }
}

and finally get the following error:

{"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client credentials missing or malformed in both HTTP Authorization header and HTTP POST body."}

In Postman the connection is made successfully.Connection with Postman

link to Oauth provider Api Doc


Solution

  • There are a couple of issues with your code.

    Primarily, you are not including the client_id and client_secret in the form. I observed with Fiddlr that Postman sends all these fields on a submitted form.

    Other changes to improve, but probably not required to fix the code are to:

    • Remove the nonsense Authorization Bearer header. Postman does not send and doesn't make sense to send one until a token has been received.

    • Replaced the home-made form emulation and replaced with the standard UrlEncodedFormEntity passing a List<NameValuePair>. It adds the "Content-Type", "application/x-www-form-urlencoded" header to the request.

    Try this:

    import java.io.IOException;
    import java.security.cert.X509Certificate;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.net.ssl.SSLContext;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.http.Consts;
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.ssl.TrustStrategy;
    
    
    public class OAuthClient {
    
        private String tokenEndpoint = "https://xxxxxxxxxxxxx/oauth2/token";
        private String client_id = "2xxxxxxxxxxxxxxxxx";
        private String client_secret = "xxxxxxxxxxxxxxxxxxxx";
        private String grant_type = "client_credentials";
        private String scope = "projects:read";
        
    
        public String callService()  {
    
    
            String result = null;
    
            try {
    
                            //My site is set as insecure, so I set the following setting
                TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
    
                SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                        .loadTrustMaterial(null, acceptingTrustStrategy)
                        .build();
    
                SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
    
                CloseableHttpClient httpClient = HttpClients.custom()
                        .setSSLSocketFactory(csf)
                        .build();
    
    
                /* HTTPCLIENT AND HTTPPOST OOBJECT */
                HttpPost httpPost = new HttpPost(tokenEndpoint);
    
                /* OAUTH PARAMETERS ADDED TO FORM */
                List<NameValuePair> form = new ArrayList<>();
                form.add(new BasicNameValuePair("grant_type", grant_type));
                form.add(new BasicNameValuePair("scope", scope));
                form.add(new BasicNameValuePair("client_id", client_id)); // these two were missing
                form.add(new BasicNameValuePair("client_secret", client_secret));
                
                // UrlEncodedFormEntity is the standard way of building a firm submission
                // and it removes the need to manually add the "Content-Type", "application/x-www-form-urlencoded" header
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(form, Consts.UTF_8);
                
                httpPost.setEntity(entity);
    
                /* SEND AND RETRIEVE RESPONSE */
                HttpResponse response = null;
                try {
                    response = httpClient.execute(httpPost);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                /* RESPONSE AS STRING */
                //      String result = null;
                try {
                    result = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            catch (Exception e) {
                // TODO: handle exception
            }
            return result;
        }
    
        public static void main(String[] args) {
            OAuthClient oauthClient = new OAuthClient();
            String res = oauthClient.callService();
            System.out.println(res);
        }
    }