Created
August 18, 2020 18:05
-
-
Save mjg123/18cb3c73d4ba530001faecb28ea98953 to your computer and use it in GitHub Desktop.
Validating the X-Twilio-Signature header with SparkJava
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.twilio.security.RequestValidator; | |
import java.util.HashMap; | |
import static spark.Spark.get; | |
import static spark.Spark.post; | |
public class RequestValidation { | |
public static void main(String[] args) { | |
setupUnvalidatedEndpoints(); | |
setupValidatedEndpoints(); | |
} | |
private static void setupUnvalidatedEndpoints() { | |
get("/unvalidated", (req, res) -> { | |
return "ok"; | |
}); | |
post("/unvalidated", (req, res) -> { | |
return "ok"; | |
}); | |
} | |
private static void setupValidatedEndpoints() { | |
var twilioAuthToken = System.getenv("TWILIO_AUTH_TOKEN"); | |
var requestValidator = new RequestValidator(twilioAuthToken); | |
// We can't pull this from request.getUrl() because any proxy or API-gateway could | |
// have rewritten it by the time the request reaches our server, so hard-code | |
// the value from the Phone Number configuration page. | |
// (Note: it may be possible to reconstruct this from the headers as a proxy | |
// might put the original URL in a header, that depends on the proxy so this | |
// is simpler) | |
String webhookUrl = "https://4567a38d8466.ngrok.io/validated"; | |
get("/validated", (req, res) -> { | |
var twilioSignature = req.headers("X-Twilio-Signature"); | |
var isValidRequest = validateRequestSignature(requestValidator, webhookUrl, req, twilioSignature); | |
if (!isValidRequest) { | |
res.status(401); | |
return "unauthorized"; | |
} | |
return "OK, you're valid"; | |
}); | |
post("/validated", (req, res) -> { | |
var twilioSignature = req.headers("X-Twilio-Signature"); | |
var isValidRequest = validateRequestSignature(requestValidator, webhookUrl, req, twilioSignature); | |
if (!isValidRequest) { | |
res.status(401); | |
return "unauthorized"; | |
} | |
return "OK, you're valid"; | |
}); | |
} | |
private static boolean validateRequestSignature(RequestValidator requestValidator, String webhookUrl, spark.Request req, String twilioSignature) { | |
var validationParams = new HashMap<String, String>(); | |
if (req.requestMethod().equals("GET")){ | |
// for GET requests, add the query string but don't add any params | |
// this will fail if the URL as provided in the PN config page already | |
// has query params. | |
webhookUrl += "?" + req.queryString(); | |
} else { // POST | |
// Query params can (in theory) have multiple values. Twilio doesn't | |
// actually send any repeated values, hence `v[0]` below. | |
req.queryMap().toMap().forEach((k, v) -> validationParams.put(k, v[0])); | |
} | |
return requestValidator.validate( | |
webhookUrl, | |
validationParams, | |
twilioSignature); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment