I'm falling into a problem this morning with a custom request between two application, what i need to do is to let application able to talk eachother with two Rest API cause i need to do some actions on the first application by the second. The two applications are developed with springboot.
Suppose to call this two applications admin and superadmin
superadmin send a request with a RestAPI and a customized header -> name = key value = 1234
admin recieve the request and first of all check if the header is present or not, after that the header is finded it can proceed to do all the task.
Here's the code that i've developed :
SUPERADMIN :
#PostMapping(value="/test_api_header")
public ResponseEntity<String> test_API(#RequestParam String url) {
RestTemplate template = new RestTemplate();
URI targetUrl = UriComponentsBuilder.fromUriString(url) // Build the base link
.path("/test_API") // Add path
.build() // Build the URL
.encode() // Encode any URI items that need to be encoded
.toUri(); // Convert to URI
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/json");
headers.add("superadminKey", "123456abc");
// build the request
ResponseEntity<String> entity = template.exchange(targetUrl, HttpMethod.GET, new HttpEntity<String>(headers), String.class);
return entity;
}
ADMIN :
#Value("123456abc")
private String saKey;
#GetMapping(value = "/superadmin/test_API")
public String test_API(HttpServletRequest request) {
if (request.getHeader("superadminKey") == saKey) {
return "Finally";
} else {
return "Nothing to do, header not present";
}
}
The SUPERADMIN is able to communicate with the RESTApi in the ADMIN application, in fact on postman i received the answer : Nothing to do, header not present, but i really cannot be able to set that customized header in the superadmin request cause i cannot found it also on postman request in the section "headers".
I've seen that i could also create a customized API Key for this special case, but really don't know how it works, if someone could help me I would be very grateful!
Related
I want to be able to fetch a param from the redirect url whenever it is automated. I am having difficulties doing this as I am getting a bad request after I created another endpoint to effect this.
I have an endpoint that works fine. The endpoint is a get method. Loading the endpoint takes a user to a page where they need to provide some necessary details. Once these details have been verified, the user is redirected to my redirecr_uri. The redirect_uri now contains important information like session_id, code, etc. The most important thing I need is the code. I need to pass the code into yet another endpoint which will return an access token.
I have manually done this process and it works but I want it to be done automatically because I can't keep doing that when I push the code to staging or production.
Here is the endpoint that redirects as well as the method.
#GetMapping("/get-token")
public RedirectView getBvn() throws UnirestException {
return nibss.getAccessToken();
}
This is the method that the controller calls
public RedirectView getAccessToken() throws UnirestException {
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/authorize.htm?scope=profile&acr_values=otp&response" +
"_type=code&redirect_uri=https://www.accionmfb.com/&client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e";
RedirectView redirectView = new RedirectView();
redirectView.setUrl(url);
return redirectView;
}
When the user provides the right information they are redirected to something like this
https://www.accionmfb.com/?code=9ad91f13-4698-4030-8a8f-a857e6a9907e&acr_values=otp&scope=profile&session_state=fa525cabc5b62854c73315d0322fd830c12a5941b89fd8e6e518da369e386572.b78a3d21-e98e-4e9a-8d60-afca779d9fad&sid=fd60ab92-ef37-4a5b-99b9-f8f52321985d
It is important to state that this 3rd party API I am trying to consume uses oath2.0 client authentication.
I created this endpoint to get the code from the redirected_uri
#GetMapping("/redirect-url")
public void handleRedirect(#RequestParam("code") String code) throws UnirestException {
if(Objects.nonNull(code) || !code.isEmpty()){
nibss.getToken(code);
log.info("Code is not being passed {}", code);
} else {
log.info("Code is not being passed {}", code);
}
}
public String getToken(String code) throws UnirestException {
log.info("This is the code here oooooooooo {}", code);
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/restv1/token";
String parameters = "client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e&code="+code+"&redirect_uri=https://www.accionmfb.com/&grant_type=authorization_code";
HttpResponse<String> apiResponse = Unirest.post(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic MDkxNWNkMDAtNjdmMi00NzY4LTk5YWMtMWIyZmY5ZjFkYTJlOlRVRnEwcGFBQXRzbzBZOEcxMkl2WFZHUmx6WG5zaERiaGt1dzI1YUM=")
.body(parameters)
.asString();
//JSONObject apiJson = apiResponse.getBody().getObject();
//return apiJson.getString("access_token");
JSONObject json = new JSONObject(apiResponse.getBody());
String accessToken = json.getString("access_token");
log.info(accessToken);
return accessToken;
}
But this is not working, I get 400 whenever I hit the second endpoint. What am I doing wrong?
The redirect_uri that you are passing to the OAuth server is https://www.accionmfb.com which does not include the path /redirect-url so the redirect never hits your method.
Either register and pass a callback uri like redirect_uri=https://www.accionmfb.com/redirect-url
Or change #GetMapping("/redirect-url") to #GetMapping("/")
For various REST api endpoints, the user_id will reach the backend, needed for further processing and then, sent back as a response to the front end.
I have a feeling I can do this through the header instead of passing it as a path parameter each time, except I can't seem to find the relevant information yet.
At the moment I send the response as a ResponseEntity. I would like, if possible, to keep this option.
I am using Java and Spring Boot.
example based on
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
edited to add readign header from request
#RequestMapping("/handle")
public ResponseEntity<String> handle(HttpServletRequest httpRequest) {
String userId= httpRequest.getHeader("user_id");
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("user_id", userId);
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
I have decided that the best approach for my scenario, where I only need to fetch the user id and then respond back with it, is to use the #RequestHeader("userId") Long userId annotation.
Let's have a look at how I had configured the enpoint initially:
#PostMapping(path = "/add-follower/{userIdForFollowing}/{currentUserId}")
public ResponseEntity<String> addFollower(#PathVariable ("userIdForFollowing") Long userIdForFollowing, #PathVariable Long currentUserId)
{
Follow newFollow = followService.returnNewFollow(userIdForFollowing, currentUserId);
newFollow = followService.saveFollowToDb(newFollow);
return new ResponseEntity<>("Follow saved successfully", HttpStatus.OK);
}
Now, let's look at how I refactored the endpoint to fetch the id's from the header and return them in the response:
#PostMapping(path = "/add-follower")
public ResponseEntity<String> addFollower(#RequestHeader("userIdForFollowing") Long userIdForFollowing, #RequestHeader("currentUserId") Long currentUserId)
{
Follow newFollow = followService.returnNewFollow(userIdForFollowing, currentUserId);
newFollow = followService.saveFollowToDb(newFollow);
//here I will add more code which should replace the String in the ResponseEntity.
return new ResponseEntity<>("Follow saved successfully", HttpStatus.OK);
}
I'm trying to use Twitter's friends list api and was successful to do so without any parameters.
However whenever I add a parameter, I would get the error "Could not authenticate you." and I have no choice but to add a cursor parameter when the friend list is too long.
The fact that I get a list of users of friends when I call the api without any parameters makes me think that authenticating the request works properly.
I have tried to change the request url to https://api.twitter.com/1.1/friends/list.json?cursor=-1 which gives me the authentication error.
I tried using both https://api.twitter.com/1.1/friends/list.json and https://api.twitter.com/1.1/friends/list.json?cursor=-1 to make oauth_signature and they both failed me.
I tried using different parameters such as screen_name or user_id and they all will give me the same error.
I even tried to add cursor: -1 header like a POST request and that didn't work either.
Right now my code looks like this
public String getFriendList() {
String baseUrl = "https://api.twitter.com/1.1/friends/list.json";
// Creates a map with all necessary headers
Map<String, String> headers = createMap();
headers.put("oauth_token", <OAuth token of user>);
String signature = createSignature("GET", baseUrl, headers, <OAuth secret of user>);
// Add oauth_signature to header
headers.put("oauth_signature", signature);
String body = sendGetRequest(baseUrl, headers);
return body;
}
public String sendGetRequest(String baseUrl, Map<String, String> parameters) throws AuthException, IOException {
try (CloseableHttpClient client = CloseableHttpClientFactory.getHttpClient()) {
HttpGet httpGet = new HttpGet(baseUrl);
if (parameters != null) {
httpGet.setHeader("Authorization", createHeader(parameters));
}
CloseableHttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != 200) {
LOGGER.info("GET Request Failed : " + EntityUtils.toString(response.getEntity()));
throw new Exception();
}
String responseBody = EntityUtils.toString(response.getEntity());
return responseBody;
}
}
which is the working code.
Could anyone tell me where to add parameters and what I have missed to authenticate the request?
EDIT : Added code of sendGetRequest. Making the signature and adding the header was made by following the documentations from twitter
I am trying to consume the following HTTPS endpoints from Yahoo Weather Service:
Yahoo Weather Service API
I am doing some special query according to the API to get the current weather at some parametrized location.
#Service("weatherConditionService")
public class WeatherConditionServiceImpl implements WeatherConditionService {
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public WeatherCondition getCurrentWeatherConditionsFor(Location location) {
RestTemplate restTemplate = new RestTemplate();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(URL);
stringBuilder.append("?q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22");
// TODO: Validate YQL query injection
stringBuilder.append(location.getName());
stringBuilder.append("%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
WeatherQuery weatherQuery = restTemplate.getForObject(stringBuilder.toString(), WeatherQuery.class);
// TODO: Test Json mapping response
Condition condition = weatherQuery.getQuery().getResults().getChannel().getItem().getCondition();
return new WeatherCondition(condition.getDate(), Integer.parseInt(condition.getTemp()), condition.getText());
}
Location is a class that provides the attribute "name" that is a String description of the location, such as "New York" or "Manila".
Condition an other classes just map the returning object.
When executing I get the following HTTP response:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
So this means I am not authorized to access the resource from what I understand.
The URL works great if I just copy & paste it in a web browser:
Yahoo Weather Query
I think that mapping is not a problem since I am not getting "400" (Bad Request) but "403" (Forbidden)
There must be some error on the way I use the RestTemplate object. I am researching but I can't find an answer.
The docs say you need an api key. But when I make a call like this:
fetch('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22nome%2C%20ak%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys')
.then(resp=> resp.json())
.then((res)=>console.log(res.query.results))
https://repl.it/NeoM
It works fine without one. Perhaps you've been blackisted for hitting the api too often.
Your code seems fine.
I finally found the answer. It finally WAS a Bad Request because I needed to pass the parameters differently (not as part of the URL).
I found the answer here. Here goes the code for my particular Yahoo Weather API call return a String (I still will have to do some work to use the mapping).
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public String callYahooWeatherApi() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(URL)
.queryParam("q", "select wind from weather.forecast where woeid=2460286")
.queryParam("format", "json");
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.GET,
entity,
String.class);
return response.getBody();
}
I'm trying to send a post request to github to create a repository. I've got oauth 2.0 working and the request is correctly signed but github is just returning "Problems parsing JSON"
I'm using Scribe for the oauth side of things and as far as I can tell I've added JSON to the URL but I'm not 100% certain I'm doing it correctly, or am I just missing headers or something?
#POST
#Path("create_repo/{userid}")
#Produces(MediaType.APPLICATION_JSON)
public Response createRepo(#PathParam("userid") String userid) {
OAuthService service = createService().build();
User user = collection.findOneById(userid);
final OAuthRequest request = new OAuthRequest(Verb.POST, "https://api.github.com/user/repos", service);
Token token = new Token(user.getGithubToken(), "SECRET");
service.signRequest(token, request);
request.addHeader("Content-type", "application/vnd.github.v3+json");
request.addHeader("X-OAuth-Scopes", "repo");
request.addQuerystringParameter("name", "Test_v1");
LOGGER.info("Built request: " + request.getCompleteUrl());
final com.github.scribejava.core.model.Response response = request.send();
return Response.ok(response.getBody()).build();
}
The built URL looks like: https://api.github.com/user/repos?access_token=XXX_SECRET_XXX&name=Test_v1
I've also tried swapping the access_token after the params but same result.
Appreciate the any help.
Well I solved this by creating a object, serializing it, and adding it as a payload.
#POST
#Path("create_repo/{userId}/{projectId}")
#Produces(MediaType.APPLICATION_JSON)
public Response createRepo(#PathParam("userId") String userId, #PathParam("projectId") String projectId) {
// Setup collections
User user = userCollection.findOneById(userId);
ProjectDescription projectDescription = projectCollection.findOneById(projectId);
// Build repository object from project description
GithubRepository repository = new GithubRepository();
repository.setName(projectDescription.getTitle());
repository.setDescription(projectDescription.getDescription());
// Serialize object
ObjectMapper mapper = new ObjectMapper();
String jsonInString = null;
try {
jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(repository);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// Build request
OAuthService service = createService().build();
final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL + "/user/repos", service);
request.addHeader("content-type", "application/json");
request.addPayload(jsonInString);
// Sign and send request
Token token = new Token(user.getGithubToken(), "secret");
service.signRequest(token, request);
request.send();
return Response.status(201).build();
}
However, I'd still like to know where I went wrong with my first attempt.
Query string parameters are ignored in POST requests. That's why it worked when passing them in the request body.
From GitHub's API Overview docs:
Parameters
Many API methods take optional parameters. For GET requests, any parameters not specified as a segment in the path can be passed as an HTTP query string parameter:
curl -i "https://api.github.com/repos/vmg/redcarpet/issues?state=closed"
In this example, the ‘vmg’ and ‘redcarpet’ values are provided for the :owner and :repo parameters in the path while :state is passed in the query string.
For POST, PATCH, PUT, and DELETE requests, parameters not included in the URL should be encoded as JSON with a Content-Type of ‘application/json’:
$ curl -i -u username -d '{"scopes":["public_repo"]}' https://api.github.com/authorizations