Pusher Invalid signature: Expected HMAC SHA256 hex digest of - java

I have JavaScript code in one HTML file from which I am calling server for authentication:
<html>
<script>
<head>
var options = { authEndpoint: "api/pusher.json?socket_id=9900&channel_name=presence-channel" }
var pusher = new Pusher('98384343434343434', options);
pusher.connection.bind('connected', function() {
console.log("connected");
socketId = pusher.connection.socket_id;
console.log("socketId" + socketId);
});
var channel = pusher.subscribe('presence-channel');
</script>
</head>
<body></body>
</html>
And on the server the code is as below:
import com.pusher.rest.Pusher;
import com.pusher.rest.data.PresenceUser;
import com.webapp.actions.BusinessApiAction;
#Path("/api/pusher")
public class PusherAction extends BusinessApiAction {
#POST
#Produces({ "application/Json", "application/xml" })
public Response pusher(#Context ServletContext context, #Context HttpServletRequest req, #Context HttpServletResponse res, #FormParam("socket_id") String socketId, #FormParam("channel_name") String channelName) throws Exception {
System.out.println("\n\n===channel==> " + channelName + "\t socket id-->" + socketId);
Pusher pusher = new Pusher("92063", "3055e2b132174078348c", "52cfe6c7ecb8420ad981");
String userId = "5433d5da97d88628ec000300";
Map<String, String> userInfo = new HashMap<>();
userInfo.put("name", "Phil Leggetter");
String authBody = pusher.authenticate(socketId, channelName, new PresenceUser(userId, userInfo));
JSONObject j = new JSONObject(authBody);
System.out.println("\n\n===authBody==> " + j.getString("auth"));
Map<String, Object> map = new HashMap<>();
Map<String, Object> channelData = new HashMap<>();
map.put("auth", j.getString("auth"));
JSONObject ch = new JSONObject(j.getString("channel_data"));
channelData.put("user_id", ch.getString("user_id"));
channelData.put("user_info", userInfo);
map.put("channel_data", ch.toString());
return sendDataResponse(map);
}
}
The returned response is 200 but Pusher is giving this error:
Error in the pusher logger--
Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"3055e2b132174078348c:980bf9a6d3a61d280d181785ccacd0e5e7999776085403f2d9bfe688842b8fe7","channel_data":"{\"user_info\":{\"name\":\"Phil Leggetter\"},\"user_id\":\"5433d5da97d88628ec000300\"}","channel":"presence-user2"}}
Pusher : Event recd : {"event":"pusher:error","data":{"code":null,"message":"Invalid signature: Expected HMAC SHA256 hex digest of 41797.10543542:presence-user2:{\"user_info\":{\"name\":\"Phil Leggetter\"},\"user_id\":\"5433d5da97d88628ec000300\"}, but got 980bf9a6d3a61d280d181785ccacd0e5e7999776085403f2d9bfe688842b8fe7"}}
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Invalid signature: Expected HMAC SHA256 hex digest of 41797.10543542:presence-user2:{\"user_info\":{\"name\":\"Phil Leggetter\"},\"user_id\":\"5433d5da97d88628ec000300\"}, but got 980bf9a6d3a61d280d181785ccacd0e5e7999776085403f2d9bfe688842b8fe7"}}}

The authBody that is returned from pusher.authenticate should provide you with everything you need to respond to the client request. You just need to make sure that sendDataResponse send the authBody back as the body of the response as JSON.
I've updated the example you've provided to remove the lines that aren't required:
#Path("/api/pusher")
public class PusherAction extends BusinessApiAction {
#POST
#Produces({ "application/Json", "application/xml" })
public Response pusher(#Context ServletContext context, #Context HttpServletRequest req, #Context HttpServletResponse res, #FormParam("socket_id") String socketId, #FormParam("channel_name") String channelName) throws Exception {
System.out.println("\n\n===channel==> " + channelName + "\t socket id-->" + socketId);
Pusher pusher = new Pusher(APP_ID, APP_KEY, APP_SECRET);
String userId = "5433d5da97d88628ec000300";
Map<String, String> userInfo = new HashMap<>();
userInfo.put("name", "Phil Leggetter");
String authBody = pusher.authenticate(socketId, channelName, new PresenceUser(userId, userInfo));
return sendDataResponse(authBody);
}
}
The pusher-rest-java library README show the additional functionality that was present isn't required:
https://github.com/pusher/pusher-rest-java#authenticating-presence-channels

Related

How to inject dynamic id for body of pact contract written in Java?

We have a put api that will update an assignment based on its id. As we should be cleaning up the data after a test, our assignment id would change after the original one is deleted, so we're trying to dynamically inject that into the body for the request from the provider side. However, we seem to be perhaps missing something here as it's not updating correctly and the request is still being triggered with the id set as the example.
This is the provider class:
#Slf4j
#Provider("Assignments API")
#Consumer("LTI-AGS-Tool")
//#PactBroker(url = BROKER_PACT_URL, authentication = #PactBrokerAuth(token = "${pactbroker.auth.token}"))
#VerificationReports(value = {"console", "markdown"}, reportDir = "target/pacts")
class PactProviderLTIAGSIT {
private HashMap<String, String> headers = new HashMap<>();
private String updateAssignmentId;
private final String SERVICE_TOKEN = "myToken";
#BeforeEach
void createTeacherAssignment() {
String assignmentBody = createBodyStringForStudentAssignmentSetup();
assignmentBody = assignmentBody.replace("CPWAG", "OTHER_TEXT_RESOURCE");
headers.put("Content-Type", "application/json");
headers.put("Authorization", "myToken");
RequestSpecification rq = Util.getRequestSpecification().baseUri(baseAssignmentUrl).headers(headers);
Response response = rq.body(assignmentBody).post();
assertEquals(201, response.getStatusCode());
updateAssignmentId = response.jsonPath().get("assignments[0].refId");
log.info("assignment id is " + updateAssignmentId);
}
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void pactTestTemplate(PactVerificationContext context, HttpRequest request) {
request.addHeader("Authorization", SERVICE_TOKEN);
logCurlFromPact(context, request);
context.verifyInteraction();
}
#BeforeEach
void before(PactVerificationContext context) {
context.setTarget(new HttpsTestTarget(BASE_PACT_TEACHER_ASSIGNMENTS_URL, 443, ""));
}
#State("Scoring info is passed between ags-tool and assignmentapi")
Map<String, Object> getScoringInfo() {
Map<String, Object> map = new HashMap<>();
map.put("assignmentId", updateAssignmentId);
return map;
}
}
And here the consumer contract:
#ExtendWith(PactConsumerTestExt.class)
class PactConsumerSendScoreIT {
private final Map<String, String> headers = new HashMap<>();
private final String path = "/v5/assignmentStatus/update";
#Pact(provider = PACT_PROVIDER, consumer = PACT_CONSUMER)
public RequestResponsePact scoreConsumerPact(PactDslWithProvider builder) {
headers.put("Content-Type", "application/json");
//Body given and returned
DslPart body = new PactDslJsonBody()
.valueFromProviderState("assignmentId", "assignmentId", "c1ef3bbf-55a2-4638-8f93-22b2916fe085")
.stringType("timestamp", DateTime.now().plusHours(3).toString())
.decimalType("scoreGiven", 75.00)
.decimalType("scoreMaximum", 100.00)
.stringType("comment", "Good work!")
.stringType("status", "IN_PROGRESS")
.stringType("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085")
.close();
return builder
.given("Scoring info is passed between ags-tool and assignmentapi")
.uponReceiving("Scoring info is passed between ags-tool and assignmentapi")
.path(path)
.method("POST")
.body(body)
.headers(headers)
.willRespondWith()
.status(201)
.body(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "scoreConsumerPact", providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3)
void runTest(MockServer mockServer) {
String updateAssignmentId = "c2ef3bbf-55a2-4638-8f93-22b2916fe085";
HashMap<String, Object> map = new HashMap<>();
map.put("timestamp", DateTime.now().plusHours(3).toString());
map.put("scoreGiven", 75.00);
map.put("scoreMaximum", 100.00);
map.put("comment", "Good work!");
map.put("status", "IN_PROGRESS");
map.put("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085");
map.put("assignmentId", updateAssignmentId);
//Mock url
RequestSpecification rq = Util.getRequestSpecification().baseUri(mockServer.getUrl()).headers(headers);
Response response = rq.body(map)
.post(path);
assertEquals(201, response.getStatusCode());
}
}
Thank you.
We figured out that the expression needed is to include the ${} (at least if it's a string).
Once we updated it to valueFromProviderState("assignmentId", "${assignmentId}", "c1ef3bbf-55a2-4638-8f93-22b2916fe085") it seemed to work.

OkHttp returns 403 as response

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.

How can I get failed HTTP request body in java?

I want to log all HTTP requests.
Normally, I can get a request body for a successful request with the code below.
But I don't get the request body if a request fails.
When I try to get the body of the failed request I only get an empty string.
How can I get failed HTTP request body in java?
-Filter class-
#Component
#Slf4j
public class HttpLogFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
response.addHeader(REQUEST_ID_PROPERTY_NAME, requestId);
ThreadContext.put(REQUEST_ID_PROPERTY_NAME, requestId);
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
HttpLogDTO httpLog = collectHttpInfo(requestWrapper, responseWrapper);
responseWrapper.copyBodyToResponse();
long endTime = System.currentTimeMillis();
httpLog.setDuration(endTime - startTime);
log.info(httpLog.toString());
}
private HttpLogDTO collectHttpInfo(HttpServletRequest request, HttpServletResponse response) {
String scheme = request.getScheme();
String host = request.getServerName();
String port = String.valueOf(request.getLocalPort());
String path = request.getServletPath();
String parameters = request.getParameterMap().keySet().stream()
.map(key -> key + "=" + request.getParameterMap().get(key)[0]).collect(Collectors.joining("&"));
Map<String, List<String>> requestHeaders = Collections.list(request.getHeaderNames())
.stream()
.collect(Collectors.toMap(
Function.identity(),
h -> Collections.list(request.getHeaders(h))
));
Map<String, String> responseHeaders = response.getHeaderNames()
.stream()
.collect(Collectors.toMap(h -> h, response::getHeader));
return HttpLogDTO.builder()
.protocol(request.getProtocol())
.remote(request.getRemoteAddr())
.method(request.getMethod())
.uri(scheme + "://" + host + ":" + port + path + (StringUtils.isNotBlank(parameters) ? "?" + parameters : ""))
.host(host)
.path(path)
.scheme(scheme)
.port(port)
.requestHeaders(requestHeaders.toString())
.requestBody(new String(((ContentCachingRequestWrapper) request).getContentAsByteArray()))
.statusCode(String.valueOf(response.getStatus()))
.statusValue(Objects.requireNonNull(HttpStatus.resolve(response.getStatus())).getReasonPhrase())
.responseHeaders(responseHeaders.toString())
.responseBody(new String(((ContentCachingResponseWrapper) response).getContentAsByteArray()))
.build();
}
}
[SOLVED]
The problem is not in the above Filter class.
The problem is that I did write the code like I don't expect body in the controller class.
Problem solved after adding my POJO class with #RequestBody annotation in the method.
Old Code
#PostMapping("/post")
public ResponseEntity post() {
throw new BusinessValidationException(BusinessValidationRule.CITY_NOT_FOUND);
}
New Code
#PostMapping("/post")
public ResponseEntity post(#RequestBody Object body) {
throw new BusinessValidationException(BusinessValidationRule.CITY_NOT_FOUND);
}

How to do GET API Request with URL params?

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

Rest Api call gives error 400 using Spring Oauth2

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

Categories