I'm trying to make a request to the Third-party API, but I'm running into some issues using OkHTTP.
I'm using AWS4Signer to sign the request. I'm able to generate the credentials for the same.
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.POST);
requestAws.setEndpoint(URI.create("third pary api call which uses https"));
requestAws.addHeader("x-amz-security-token", sessionCredentials.getSessionToken());
requestAws.addHeader("Content-Type", "application/json");
//sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(Constant.SERVICE_NAME);
signer.setRegionName(Constant.AWS_REGION);
signer.sign(requestAws, new AWSCredentials() {
#Override
public String getAWSSecretKey() {
return sessionCredentials.getAccessKeyId();
}
#Override
public String getAWSAccessKeyId() {
return sessionCredentials.getSecretAccessKey();
}
});
Map<String, String> headers = requestAws.getHeaders();
String x_date = null;
String x_token = null;
String authorization = null;
String x_content = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
logger.info("Headers body response: " + JsonUtils.jsonize(headers));
String json = objectMapper.writeValueAsString(emailRequestBody);
postHandler.post(x_date, x_token, authorization, json);
Below is the request code of okHTTP
String post(String x_date, String x_token, String authorization, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url("https url is here")
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Below is how the request looks like with headers:
Request{method=POST, url=https://cbc.com/api/send/email, headers=[Content-Type:application/json, X-Amz-Date:20220125T111056Z, x-amz-security-token:FwoGZXIvYXdzEHUaDF6/kQ0g7Mog7W1f7CK0ATG5xhFIXP34wRjziAkJKhw9vE5cbADBOpji7uqtLp5GLGLay+e9O2deFRB4eSpUMOOThDCEQg1tum43iX4a+8Kikuc3fv5gDjbMrdLJYAK3piYVbOAET8BAXdDdkPZVG+nNu31cEWZe9HC60svIj0m95YZ9Xx5rBIDm0AVWtj4JRCmonNm1ymCNRB4GTjhEzgnxlkqEYfdUivFdlORq/IlIssUzzV04fkr0kiqDiE9GrmU51ijAtb+PBjIt9MWbM8+x4z+y+IV4JFjuK4zrVW3Iaw4xUG/C+mpcCrZrunh+8fWgVTR6In1r, Authorization:AWS4-HMAC-SHA256 Credential=medS2y7xvISbOf7ke3IWthyCMV5koeTDD5r3gkxJ/20220125/us-west-2/execute-api/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token, Signature=d862c9ed8175770244e17fd3cb216c2a92138183ad427ed67fc5f284a1a75266]}
Below is the response:
Response{protocol=h2, code=403, message=, url=https://cbc.com/api/send/email}
Why the response is returning 403? Can someone help me what I missed? Thank you for your time.
Related
I'm trying to get an Access Token from Keycloak over SpringBoot and did try the following example. But the KeycloakAuthenticationToken token is null.
Does someone know another approach to get an Access Token?
#GetMapping("/token")
public String getToken(HttpServletRequest request) throws IOException {
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) request.getUserPrincipal();
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) token.getAccount().getKeycloakSecurityContext();
KeycloakSecurityContext context = token.getAccount().getKeycloakSecurityContext();
String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(session.getToken());
String idTokenPretty = JsonSerialization.writeValueAsPrettyString(session.getIdToken());
RefreshToken refreshToken;
try {
refreshToken = new JWSInput(session.getRefreshToken()).readJsonContent(RefreshToken.class);
} catch (JWSInputException e) {
throw new IOException(e);
}
String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
return refreshTokenPretty;
}
Seems like I can get a token like this with ('org.keycloak:keycloak-admin-client'):
Keycloak keycloak = KeycloakBuilder.builder() //
.serverUrl(serverUrl) //
.realm(realm) //
.grantType(OAuth2Constants.PASSWORD) //
.clientId(clientId) //
.clientSecret(clientSecret) //
.username(userName) //
.password(password) //
.build();
AccessTokenResponse tok = keycloak.tokenManager().getAccessToken();
If someone knows a more elegant way, I would appreciate if you let me know :)
Thanks in advance!
Try the following:
HttpEntity<MultiValueMap<String, String>> request =
new TokenRequest.Builder(clientID, OAuth2Constants.PASSWORD)
.add("username", userName)
.add("password", password)
.build();
ResponseEntity<String> response = restTemplate.postForEntity( postUrl, request , String.class );
return response.getBody();
and the helper class:
public class TokenRequest {
public static class Builder{
MultiValueMap<String, String> data;
public Builder(String clientID, String grant_type){
data = new LinkedMultiValueMap<>();
data.put("client_id", Collections.singletonList(clientID));
data.put("grant_type", Collections.singletonList(grant_type));
}
public Builder add(String key, String value){
data.put(key, Collections.singletonList(value));
return this;
}
public HttpEntity<MultiValueMap<String, String>> build(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
return new HttpEntity<>(data, headers);
}
}
private TokenRequest(){
}
}
Try this:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
KeycloakAuthenticationToken keycloakAuthenticationToken = (KeycloakAuthenticationToken) request.getUserPrincipal();
KeycloakPrincipal<KeycloakSecurityContext> principal = (KeycloakPrincipal) keycloakAuthenticationToken.getPrincipal();
String token = principal.getKeycloakSecurityContext().getIdTokenString();
I'm trying to run below test :
#Test(priority =1)
public void PostNetwork() throws ClientProtocolException, IOException {
String postBody = new JSONObject(){{
put("vlan", TestHelper.randomVlan());
put("computeNodeGroups", new int[]{});
put("name", TestHelper.randomString(5));
put("virtualNetworkGroups",new int[]{});
put("colocationPorts",new int[]{});
}}.toString();
System.out.println("POST BODY: " + postBody);
Object response = super.post(postnetworks, postBody);
//Assert.assertNotNull(response.createdDate);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>"+postnetworks);
System.out.println(
"-----------------------------------------");
}
and this is the main test :
public PostResponseType post(String url, String postBody, boolean isNegative, int expectedStatusCode) throws ClientProtocolException, IOException {
HashMap<String, String> headerMap = new HashMap<String, String>();
headerMap.put("Content-Type", "application/json");
headerMap.put("Authorization", "Bearer " + TestBase.getAccessToken(isNegative));
System.out.printf("Sending request to %s\n", url);
// Call the API
CloseableHttpResponse closeableHttpResponse = restClient.post(url, postBody, headerMap);
// Assert that the request was successful
int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
//Assert.assertEquals(statusCode, expectedStatusCode);
// If the request fails, don't check the object structure
if (expectedStatusCode / 100 != 2) {
return null;
}
Header[] headersArray = closeableHttpResponse.getAllHeaders();
// Print headers and response for debugging
HashMap<String, String> allHeaders = new HashMap<String, String>();
for (Header header : headersArray) {
allHeaders.put(header.getName(), header.getValue());
}
System.out.println("Header Array-->" + allHeaders);
System.out.println("statusCode: " + statusCode);
String responseString = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
System.out.println("The response from post API: " + responseString);
try {
PostResponseType returnvalue = MAPPER.readValue(responseString, this.postResponseClass);
return returnvalue;
} catch (Exception e) {
return null;
}
}
Getting this on the console :
POST BODY:
{"vlan":2400,"computeNodeGroups":[],"name":"onkhw","virtualNetworkGroups":[],"colocationPorts":[]}
statusCode: 400
The response from post API: Error
at BadRequestError.HttpError [as constructor] (/node_modules/routing-controllers/http-error/HttpError.js:27:23)
at new BadRequestError (/node_modules/routing-controllers/http-error/BadRequestError.js:20:28)
at VirtualNetworkController. (/home/site/wwwroot/app/controllers/virtual-network-controller.js:103:23)
at Generator.next ()
at fulfilled (/home/site/wwwroot/app/controllers/virtual-network-controller.js:16:58)
at
at process._tickCallback (internal/process/next_tick.js:188:7)
But the post is really happening on the UI , so how can I get the 200 status since it is really doing the post.
Client client = ClientBuilder.newClient();
urlApi="https://localhost:123/demo/api/v1/rows/search?";
WebTarget webTarget = client.target(urlApi);
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
webTarget.queryParam(entry.getKey(), entry.getValue());
}
webTarget.queryParam("searchConditions",webTarget.queryParam("mobileNo","+9999999999"));
Invocation.Builder builder = webTarget.request();
builder.header("id", "ABC");
String asB64 = Base64.getEncoder().encodeToString("ABC:PWD".getBytes("utf-8"));
logger.debug("Calling API "+urlApi);
builder.header("Authorization", "Basic "+asB64);
builder.header("Content-type", MediaType.APPLICATION_JSON);
response = builder.get();
responseData = response.readEntity(String.class);
System.out.println(responseData);
I am trying to do GET request with searchCondition as Key and value as {mobileNo="+919999999999"}, I am unable to get this to work.
Apart from that, how can I print the Request "Headers" along with "Query params"? Thank you in advance
I think you need to encode the value inputs, something like this:
webTarget.queryParam("searchCondition", URLEncoder.encode("{mobileNo=\"+919999999999\"}", StandardCharsets.UTF_8.toString()));
UDPATE:
Example of the rest client with Spring:
#Test
public void testStack() throws Exception {
RestTemplate rest = new RestTemplate();
String fooResourceUrl="http://localhost:8080/usersParam?";
RestTemplate restTemplate = new RestTemplate();
String parameter = "{mobileNo=\"+919999999999\"}";
ResponseEntity<String> response = restTemplate.getForEntity(fooResourceUrl + "parameter=" + URLEncoder.encode(parameter, StandardCharsets.UTF_8.toString() ), String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
And this would be the rest service:
#RequestMapping(method = RequestMethod.GET, value="/usersParam")
public User getUsersInfo(#RequestParam String parameter) throws UnsupportedEncodingException {
System.out.println(URLDecoder.decode(parameter, StandardCharsets.UTF_8.toString() ));
return null;
}
I'm facing a different Api Service which I have to request using POST but with no body content, I'm sending a image converted to base64, I've been searching about that issue and I found this "solution", which it didn't work :
1 :
RequestBody reqbody = RequestBody.create(null, new byte[0]);
Request.Builder formBody = new Request.Builder().url(url).method("POST",reqbody).header("Content-Length", "0");
2 :
request = new Request.Builder()
.url(BASE_URL + route)
.method("POST", RequestBody.create(null, new byte[0]))
.post(requestBody)
.build();
Even I explicit saying that is a POST method, it keeps send a GET request and not a POST request. Thanks!
My Activity :
public String SendImage(String image64) throws IOException{
//RequestBody reqbody = RequestBody.create(null, new byte[0]);
Request request = new Request.Builder()
.url("http://ap.imagensbrasil.org/api/1/upload/?key=9c9dfe77cd3bdbaa7220c6bbaf7452e7&source=" + image64 + "&format=txt")
.method("POST", RequestBody.create(null, new byte[0]))
.header("Content-Length", "0")
.build();
OkHttpClient Client = client.newBuilder() .readTimeout(25, TimeUnit.SECONDS).build();
Response response = Client.newCall(request).execute();
return response.body().string();
}
It is working on retrofit , so if you continue with Retrofit v2.0 you can use this :
public class Base64EncodeRequestInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder();
if (originalRequest.method().equalsIgnoreCase(POST)) {
builder = originalRequest.newBuilder()
.method(originalRequest.method(), encode(originalRequest.body()));
}
return chain.proceed(builder.build());
}
private RequestBody encode(RequestBody body) {
return new RequestBody() {
#Override
public MediaType contentType() {
return body.contentType();
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
Buffer buffer = new Buffer();
body.writeTo(buffer);
byte[] encoded = Base64.encode(buffer.readByteArray(), Base64.DEFAULT);
sink.write(encoded);
buffer.close();
sink.close();
}
};
}
}
I'm building a rest API using Spring security Oauth2 to secure it.
The following curl command runs succesfully and I get the token:
curl -X POST -vu clientapp:123456 http://localhost:8080/dms-application-0.0.1-SNAPSHOT/oauth/token -H "Accept: application/json" -d "password=spring&username=roy&grant_type=password&scope=read%20write&client_secret=123456&client_id=clientapp"
The following test to get the token also runs succesfully:
#Test
public void getAccessToken() throws Exception {
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";
// #formatter:off
String content = mvc
.perform(
post("/oauth/token")
.header("Authorization", authorization)
.contentType(
MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "roy")
.param("password", "spring")
.param("grant_type", "password")
.param("scope", "read write")
.param("client_id", "clientapp")
.param("client_secret", "123456"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.access_token", is(notNullValue())))
.andExpect(jsonPath("$.token_type", is(equalTo("bearer"))))
.andExpect(jsonPath("$.refresh_token", is(notNullValue())))
.andExpect(jsonPath("$.expires_in", is(greaterThan(4000))))
.andExpect(jsonPath("$.scope", is(equalTo("read write"))))
.andReturn().getResponse().getContentAsString();
// #formatter:on
String token= content.substring(17, 53);
}
However, when calling the rest end point externally from a webapp using Spring RestTemplate gives me a http error 400.
Below the code:
#RequestMapping(value = "/authentication", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity authenticate(#RequestBody CredentialsDto credentials) {
try {
String email = credentials.getEmail();
String password = credentials.getPassword();
String tokenUrl = "http://" + env.getProperty("server.host") + ":8080" + "/dms-application-0.0.1-SNAPSHOT" + "/oauth/token";
// create request body
JSONObject request = new JSONObject();
request.put("username", "roy");
request.put("password", "spring");
request.put("grant_type","password");
request.put("scope","read write");
request.put("client_secret","123456");
request.put("client_id","clientapp");
// set headers
HttpHeaders headers = new HttpHeaders();
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_FORM_URLENCODED.toString();
headers.set("Authorization",authorization);
headers.set("Accept","application/json");
headers.set("Content-Type",contentType);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> loginResponse = restClient.exchange(tokenUrl, HttpMethod.POST, entity, String.class);
// restClient.postForEntity(tokenUrl,entity,String.class,)
if (loginResponse.getStatusCode() == HttpStatus.OK) {
//JSONObject userJson = new JSONObject(loginResponse.getBody());
String response = loginResponse.getBody();
return ResponseEntity.ok(response);
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// nono... bad credentials
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
return null;
}
And the error I get:
"Missing grant type"
Any ideas of what can be wrong or any other ways to do it? Because I'm completely stuck on this.
Thank you
Try to do it like this:
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", "roy");
map.add("password", "spring");
map.add("grant_type", "password");
map.add("scope", "read write");
map.add("client_secret","123456");
map.add("client_id","clientapp");
HttpEntity request = new HttpEntity(map, headers);
One more thing, when you ask for a token make sure not to send a json request, but with this header:
headers.add("Content-Type", "application/x-www-form-urlencoded");