Prepare your receiving application to receive a webhook - JWT header
  • 2 minute read
  • Contributors

Prepare your receiving application to receive a webhook - JWT header


Article summary

Following is sample code for when you use a JWT signed header to send call data.

Java

import com.google.common.base.Charsets;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;

@RestController
@RequestMapping(value = "/webhook", name = "Rule engine test webhook")
public class SampleWebhook {

    private static Logger logger = LoggerFactory.getLogger(SampleWebhook.class);

    @RequestMapping(value = "/sample/newCallFromGong", method = RequestMethod.POST)
    public void newCallFromGong(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        try {
            String authorizationHeader = httpServletRequest.getHeader("Authorization");
            assert !StringUtils.isBlank(authorizationHeader);

            JWTClaimsSet claimSet = validateAndGetTokenClaims(authorizationHeader,
                    new RSASSAVerifier((RSAPublicKey) wrapPublicKey(Base64.getDecoder().decode(getPublicKey()))));
            assert httpServletRequest.getRequestURL().toString().equals(claimSet.getStringClaim("webhook_url"));

            String payload = IOUtils.toString(httpServletRequest.getInputStream(), Charsets.UTF_8);
            assert DigestUtils.sha256Hex(payload).equals(claimSet.getStringClaim("body_sha256"));
            logger.debug("Payload: {}", payload);
        } catch (Exception e) {
            logger.error("Failed to process new call event.", e);
        }
    }

    private PublicKey wrapPublicKey(byte[] keyBytes) throws Exception {
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SunRsaSign");
        return keyFactory.generatePublic(publicKeySpec);
    }

    private JWTClaimsSet validateAndGetTokenClaims(String jwtToken, JWSVerifier verifier) throws Exception {
        SignedJWT decodedJwsObject = SignedJWT.parse(jwtToken);
        boolean verified = decodedJwsObject.verify(verifier);

        if (verified) {
            JWTClaimsSet claims = decodedJwsObject.getJWTClaimsSet();
            final Instant expirationTime = claims.getExpirationTime().toInstant();
            if (Instant.now().isAfter(expirationTime))
                throw new RuntimeException("Expired Token Exception");
            return claims;
        } else {
            throw new RuntimeException("Invalid Signature Exception");
        }
    }

    private String getPublicKey() {
        return "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxfj3V1rUOJJE2RBZrWSe8UAjEL6za9+XIBTdyYbEEpzmthys8qglYDX8PLimC79VjE1QK/XWmO8lTCbacYKNLRBLxh6SpF+3d6fDtg3HeaByH3iN2HhB5aEQCRbMOIiGgMEuVf1e9rdn0gBjTWYn7JWm7CHGZpA6j0RyaKqGjZVftZGhP/lmUZVJCDfS1mntd2aX738RNjU7jxCkGHYMizVSECcN0ZH3q55YW1iZjQiXcV1MHCpm3b9q8cKRVnluUwy9jwabLY4EAJI/rccg245uYivW06rAF4BOhVtnrkSebf85tRQFNH5bLdz7mI86AyUw9sA2FEW3JT2gi+qIFQIDAQAB";
    }
}

Python

import SimpleHTTPServer
import SocketServer
import jwt
import hashlib

PORT = 7080

PUBLIC_KEY = "\n".join(['-----BEGIN PUBLIC KEY-----', 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxfj3V1rUOJJE2RBZrWSe8UAjEL6za9+XIBTdyYbEEpzmthys8qglYDX8PLimC79VjE1QK/XWmO8lTCbacYKNLRBLxh6SpF+3d6fDtg3HeaByH3iN2HhB5aEQCRbMOIiGgMEuVf1e9rdn0gBjTWYn7JWm7CHGZpA6j0RyaKqGjZVftZGhP/lmUZVJCDfS1mntd2aX738RNjU7jxCkGHYMizVSECcN0ZH3q55YW1iZjQiXcV1MHCpm3b9q8cKRVnluUwy9jwabLY4EAJI/rccg245uYivW06rAF4BOhVtnrkSebf85tRQFNH5bLdz7mI86AyUw9sA2FEW3JT2gi+qIFQIDAQAB', '-----END PUBLIC KEY-----'])

class MySimpleHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

        def do_POST(self):
            data_string = self.rfile.read(int(self.headers['Content-Length']))
            decoded_jwt = jwt.decode(str(self.headers['Authorization']), PUBLIC_KEY, algorithms=['RS256'])
            m = hashlib.sha256()
            m.update(data_string)
            assert m.hexdigest() == decoded_jwt['body_sha256'], "Request body sha256 isn't equal to sha256 from jwt payload"

            self.end_headers()

httpd = SocketServer.TCPServer(("", PORT), MySimpleHTTPRequestHandler)
httpd.serve_forever()

Was this article helpful?

ESC

Eddy AI, a genAI helper, will scrub our help center to give you an answer that summarizes our content. Ask a question in plain language and let me do the rest.