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
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);
}
}