Skip to content

Instantly share code, notes, and snippets.

@renzihui
Created February 7, 2021 07:25
Show Gist options
  • Save renzihui/c44c296114712cd9a7101a82a5404146 to your computer and use it in GitHub Desktop.
Save renzihui/c44c296114712cd9a7101a82a5404146 to your computer and use it in GitHub Desktop.
Facebook Data Deletion Request Callback in Java
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
@Controller
public class FacebookDataDeletionCallbackController {
private static final Logger LOGGER = LoggerFactory.getLogger(FacebookDataDeletionCallbackController.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private String facebookAppSecret;
private String URL = "https://example.com";
/**
* https://developers.facebook.com/docs/development/create-an-app/app-dashboard/data-deletion-callback
*/
@Autowired
public FacebookDataDeletionCallbackController(@Value("${facebook.app.secret}") String facebookSecret) {
this.facebookAppSecret = facebookSecret;
}
@RequestMapping(value = "/remove_fb_data", method = RequestMethod.POST, produces = "application/json")
@ResponseBody
public Object removeApp(@RequestParam(value = "signed_request") String signedRequest) throws Exception {
String status_url = "https://www.<your_website>.com/deletion?id=abc123"; // URL to track the deletion
String confirmation_code = "abc123"; // unique code for the deletion request
Map<String, Object> data = parseSignedRequest(signedRequest);
String user_id = (String) data.get("user_id");
// Start data deletion
return Map.of("url", status_url, "confirmation_code", confirmation_code);
}
private Map<String, Object> parseSignedRequest(String signedRequest) throws Exception {
int split = signedRequest.indexOf('.');
String encoded_sig = signedRequest.substring(0, split);
String payload = signedRequest.substring(split + 1);
// decode the data
byte[] sig = Base64.getUrlDecoder().decode(encoded_sig);
Map<String, Object> data = objectMapper.readValue(Base64.getUrlDecoder().decode(payload), new TypeReference<Map<String, Object>>() {
});
// confirm the signature
byte[] expected_sig = hash_hmac(payload.getBytes(StandardCharsets.UTF_8));
if (!Arrays.equals(sig, expected_sig)) {
LOGGER.error("Bad Signed JSON signature!");
return Map.of();
}
return data;
}
public byte[] hash_hmac(byte[] bytes) throws Exception {
Key sk = new SecretKeySpec(facebookAppSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk);
return mac.doFinal(bytes);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment