Java 11 HttpClient - Missing form parameter: grant_type

Viewed 2435

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.

3 Answers

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

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")
    ...

I got this problem recently, a was using a Map to pass the parameters, and with Map I was getting 'missing grant_type error', when I changed the parameter's type to MultiValueMap, it worked.

Related