In order to authenticate that your publicly exposed endpoint is indeed being called by a Penbox webhook, the HTTP call needs to be authenticated. This can be done by verifying the JWT signature sent along in the HTTP headers. Here are the steps needed to properly authenticate an incoming HTTP request:

📘

Assuming you system is configured with an <issuer_origin> equal to https://connect.penbox.io/ in production

First, verify the integrity of the payload

  • As the request streams into you server, or after you received the full payload, you can compute its sha-512 digest. The base64 encoded digest of the (uncompressed) raw body (the plain JSON) must match the digest claim from the signature.
  • For conveniency this digest can also be found in the [Digest header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest), allowing to check the validity in two (independent) phases:
    • Check that the jwt is valid and ensure that its digest claim matches the value from the Digest header.
    • Ensure that the sha-512 hash of the http payload matches the value from the Digest header.

Secondly, verify the authenticity of the HTTP request by validating the JWT contained in the x-pnbx-signature header.

  • The iss claim of the JWT payload must exactly match the configured <issuer_origin>
  • The exp claim, if present, must be a timestamp (seconds since epoch) that is "in the future" wrt. the time at which the HTTP request is being made.
  • The nbf claim, if present, must be a timestamp (seconds since epoch) that is "in the past" wrt. the time at which the HTTP request is being made.
  • The aud claim must exactly match the endpoint being called (your endpoint's public address)
  • The method claim must exactly match the method use by the current HTTP request
  • The digest claim must exactly match the <base64_sha-512_digest> of the request body.
  • The signature part of the JWT must be validated against the public key of the issuer.
    • The public key can be retrieved in the JWKS format at the following endpoint <issuer_origin>/.well-known/jwks.json
    • You might want to cache this value (respecting the Cache-Control header)
  • For additional security, you can protect agains "replays" of the exact same HTTP requests content by ensuring that the value contained in the jti claim is a value that was never seen by your server before (no need to keep this value longer than the token's expiration date).