I'm trying to get an access token from Keycloack server via HttpClient of Java 11. But I keep getting an error:
{
"error":"invalid_request",
"error_description":"Missing form parameter: grant_type"
}
By the way, with postman, I can get the token. What I'm doing in my code is:
Map<String, String> values = new HashMap<>() {{
put("username", username);
put ("password", password);
put("grant_type", "password");
}};
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(values);
HttpRequest request=HttpRequest.newBuilder()
.uri(URI.create(tokenEndpoint))
.setHeader("client_id", clientId)
.setHeader("client_secret", clientSecret)
.setHeader("content-type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
/*
//Tried also
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
*/
What am I doing wrong or missing?
Keycloak Missing form parameter: grant_type not worked.
From the line .setHeader("content-type", "application/x-www-form-urlencoded"), the request is expecting form data,
but you are sending json in the request body (via ObjectMapper.writeValueAsString()).
If you are trying to send form data, then this should be the correct encoding:
String requestBody = "username=" + username + "password=" + password + "grant_type=password"
If the API is expecting JSON, then change the content type header to application/json:
HttpRequest request=HttpRequest.newBuilder()
...
.setHeader("content-type", "application/json")
...
This is how we done login:
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("client_id", keycloakProperties.getResource());
requestParams.add("username", username);
requestParams.add("password", password);
requestParams.add("grant_type", "password");
requestParams.add("client_secret", String.valueOf(keycloakProperties.getCredentials().get("secret")));
requestParams.add("scope", "openid");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
String url = keycloakProperties.getAuthServerUrl() + "/realms/" + keycloakProperties.getRealm() + "/protocol/openid-connect/token";
AccessTokenResponse keycloakAccessToken = getAccessTokenResponse(request, url);
// sometimes SSL handshake was failing, so catching error and trying again :)
private AccessTokenResponse getAccessTokenResponse(HttpEntity<MultiValueMap<String, String>> request, String url) {
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (ResourceAccessException e) {
log.error("KeyCloak getAccessTokenResponse: " + e.getMessage());
try {
ResponseEntity<AccessTokenResponse> response = restTemplate.postForEntity(url, request, AccessTokenResponse.class);
return response.getBody();
} catch (Exception ex) {
throw ex;
}
} catch (Exception e) {
throw e;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With