RestTemplate POST form URL encoded data giving 400 error - java

I'm using Spring's (version 2.5.4) RESTTemplate to make a POST call to a REST end point (url). Here is the code I have:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String,String> requestBody = new LinkedMultiValueMap<>();
requestBody.add("token", "abc");
HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(requestBody, headers);
try
{
ResponseEntity<TokenValidationResponse> responseEntity = restTemplate.exchange(url, HttpMethod.POST, request, TokenValidationResponse.class);
TokenValidationResponse response = responseEntity.getBody();
Boolean isActive = response.getActive();
if(isActive == null || !isActive) {
log.info("The token supplied isn't active");
}
}
catch(RestClientException e)
{
log.error("An error occurred while posting query to security URI for token validation", e);
}
I keep running into a 400 bad request -
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [{"error_description":"token parameter is required for the security endpoint.","error":"invalid_request"}]
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:101)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:186)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:125)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:819)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602)
This is the requirement given for the security endpoint url - This endpoint accepts only the HTTP POST method. The required Content-Type value is application/x-www-form-urlencoded. The endpoint has the following parameters: token
I can see the token being passed in though. I am able to verify that the call works fine on POSTMAN when I supply the token as part of the body. I was wondering if there is something I am missing?

You need to specify the token in the headers for example:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);
I suggest to see this helpfull post

Related

Keycloak spring - incorrect URI

I implemented a KeyCloak client with the following configuration:
keycloak configuration
And I implemented my callback endpoint like that:
#GetMapping("/callback")
#ResponseBody
public String getToken(#RequestParam String code) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED.toString());
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("code", code);
map.add("client_id", "spring-login-app");
map.add("client_secret", "");
map.add("grant_type", "authorization_code");
map.add("redirect_uri", UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:3002/callback").build().toString());
HttpEntity formEntity = new HttpEntity<MultiValueMap<String, String>>(map, headers);
try {
ResponseEntity<KeycloakTokenResponse> response =
restTemplate.exchange("http://127.0.0.1:8080/auth/realms/raroc/protocol/openid-connect/token",
HttpMethod.POST,
formEntity,
KeycloakTokenResponse.class);
KeycloakTokenResponse resp = response.getBody();
return resp.getAccess_token();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "nothing";
}
The problem is when I tried to get my access token from this callback endpoint, I received a 400 Bad Request error with the following message: 400 Bad Request: "{"error":"invalid_grant","error_description":"Incorrect redirect_uri"}"
When I test it through postman with the same x-www-form-url-encoded form params, it works fine, but in spring, it's impossible to do it.
I tried many scenario for the "redirect_uri" param, just a String, an UriComponentsBuilder.formHttpUrl, some other URL encoder thing but unfortunately I still have this error.
You can try to specify a: http://localhost:3002/* instead of your actual redirect URI in the KeyCloak configuration but from what I read in your settings, everything looks good.
Be careful also sometimes if you are changing the configuration of Keyloak, you need to restart it to take the changes into account.
If you want to test also a full scenario, open an incognito tab with your browser, and it should work.

Spring Boot: Basic Auth Returning 401 But Works in Postman

I am trying to consume a secured API using basic auth, but I keep getting back 401 error. The issue is the credentials and requested URL connect successfully when using Postman. Here is the call I am making, not sure if there is anything I am missing:
public ResponseEntity randomSht(){
String username = "APIKEY";
String password = ":PASSWORD";
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
// request url
String url = "https://test.myshopify.com/admin/orders/2013413015622.json";
RestTemplate restTemplate = new RestTemplate();
HttpEntity request = new HttpEntity(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, request, String.class);
System.out.println(response);
return response;
}
maybe your password is just PASSWORD, you don't need to add the :, if this is just the devider between header name and value.

restTemplate exchange often leads into 400 Error

In my Android app I try to make a GET request via restTemplate.exchange but it leads very often into a 400 error very seldom it's a 200 response.
GET request for "http://someURL/items/modified/2018-12-20T12%253A47%253A43%252B01%253A00" resulted in 400 (); invoking error handler
org.springframework.web.client.HttpClientErrorException: 400
I tried to do the request with encoded and decoded parameter but it's the same problem. The only thing what changes is the timestamp in the request. I don't think it's a backend problem, because I did a couple requests via Swagger and Postman on the same interface and all of them worked without a problem. I also tried to update spring-android to version 2.0.0.M3 but still the same problem.
String url = ServiceAppConstants.HOSTNAME + ServiceAppConstants.REST_ITEMS_MODIFIED + URLEncoder.encode(lastSynchronisationDate);
try {
HttpEntity<String> httpEntity = RestServiceUtils.getHttpEntity(context);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// runs in the error here
ResponseEntity<ArrayList> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, ArrayList.class);
items= response.getBody();
items = mapper.convertValue(items, new TypeReference<List<Items>>(){});
} catch (RestClientException e) {
/* do stuff */
}
to set the token
#NonNull
public static HttpEntity<String> getHttpEntity(Context context) {
UserStorage userStorage = new UserStorage(context);
HttpHeaders headers = new HttpHeaders();
try {
String token = userStorage.getJsonWebToken();
headers.set(ServiceAppConstants.HEADER_SECURITY_TOKEN, token);
}catch (Exception ex){
Log.e(RestServiceUtils.class.getName(), "Could not get json web token", ex);
}
return new HttpEntity<String>("parameters", headers);
}
This is how the request looks like in the android profiler
This is how the request looks like if it's send by swagger
use new HttpEntity(headers); (without "parameters")
the "parameters" string is the request body according to HttpEntity documentation
that might caused the problem.

Revoke token. RestTemplate using issue

I'm trying to revoke google token following API ("Content-type:application/x-www-form-urlencoded", request type POST, https://accounts.google.com/o/oauth2/revoke?token={token}).
My code example is:
private static final String REVOKE_URL = "https://accounts.google.com/o/oauth2/revoke";
private static final String TOKEN = "token";
#Autowired
private RestTemplate restTemplate;
public void revokeToken(TokenDetailsDto tokenDetailsDto) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
Map<String, String> parameters = new HashMap<>();
parameters.put(TOKEN, tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity, uriParameters);
restTemplate.exchange(REVOKE_URL, HttpMethod.GET, entity, Void.class, parameters);
}
result is exception:
17:30:54,323 INFO # com.nextiva.calendar.client.google.GoogleRevokeAccessService.revokeToken used parameters:
url=https://accounts.google.com/o/oauth2/revoke,
entity=<{Content-Type=[application/x-www-form-urlencoded]}>,
uriParameters={token=1/mzMH8VPrltarCo3LOHU_tBWi4qK5e20uIM0R_Al9T34}
17:31:20,718 ERROR # com.nextiva.calendar.web.error.handling.BaseExceptionHandler.exception 400 Bad Request
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:540)
I try use rest client to handle request like in documentation it is ok (using rest client I tried to use POST request with specified content type).
Same error result if I use
restTemplate.postForEntity(REVOKE_URL, entity, Void.class, uriParameters);
So issue is in restTemplate using (my spring part). Is any ideas, what I've done wrong here?
Solution is:
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(REVOKE_URL)
.queryParam("token", tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity);
restTemplate.postForObject(builder.build().encode().toUri(), entity, Void.class);
Small explanation: service expect urlParameters, but parameters was not urlParameters, its request parameters. So I used UriComponentsBuilder to build correct request.

Bad request converting curl http request to Java

I've the following request with curl that talks to Microsoft Azure services without a problem.
curl --request POST https://login.microsoftonline.com/common/oauth2/v2.0/token --data 'client_id=fe37...06-566f5c762ab2&grant_type=authorization_code&client_secret=tPv..dQfqomaG&scope=mail.read&code=OAQABAAIA...gAA'
Here is the java code that is throwing Bad Request exception:
public String getToken(String authCode){
try {
HttpHeaders headers = new HttpHeaders();
String url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
headers.add("client_id", "fe3..b2");
headers.add("client_secret", "tP..aG");
headers.add("grant_type", "authorization_code");
headers.add("code", authCode);
headers.add("scope", "mail.read");
HttpEntity<?> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> response = restTemplate.exchange(builder.build().toUri(), HttpMethod.POST, entity, String.class);
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
I've also tried adding the --data section in to parameters object and I receive the same problem. I am using RestTemplate but I am open for other suggestions.
I appericiate your help.
I suppose that problem is that in curl example you pass these parameters inside POST body, while in your java code you use headers instead. Try change it to usage of body params of entity object:
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("client_id", "fe3..b2");
// ... rest params
// Note the body object as first parameter!
HttpEntity<?> entity = new HttpEntity<Object>(body, new HttpHeaders());
You need to send these parameters in the request entity formatted as form url encoded and also set the content-type to application/x-www-form-urlencoded.
Your body can be a string (according to your example):
String data = "client_id=fe37...06-566f5c762ab2&grant_type=authorization_code&client_secret=tPv..dQfqomaG&scope=mail.read&code=OAQABAAIA...gAA";
HttpEntity<String> entity = new HttpEntity<>(data);
Set a content type header:
headers.add("Content-Type", "application/x-www-form-urlencoded");
(Actual implementation depends on the library you use)

Categories

Resources