Verifying webhook requests
Clients can code webhooks (user-defined HTTP calbacks) to interact with the Pismo platform during certain operations. For example, Pismo could request approval from you to authorize a purchase. For information on possible webhooks you can code and implement with Pismo, see Client webhooks.
Webhook requests use JSON Web Tokens (JWTs). Each token is signed with a public/private key pair. Pismo retains the private key and shares the public key with clients. Each token also includes a body hash you can use to verify payload integrity.
Verification process
The following diagram summarizes the process of verifying a webhook request from the Pismo platform.
A request received from Pismo contains a JWT. The JWT contains a signature, along with the name of a public key that you can use to validate the signature.
-
Check the cache to see if it contains a public key with that name. If not, request a new list of public keys from the Pismo platform.
-
Use the public key to verify the signature. If successfully verified, you can be sure the request came from Pismo.
-
Verify that the message payload has not been changed in route to your server. To do this, the JWT includes a hash. Run the message payload through SHA256 encryption. The result should match the hash.
The following sections provide a more detailed explanation of these steps.
Parsing the JWT
When the Pismo platform sends an HTTP request to a client server, the request includes an Authorization
header containing a JWT. You can use this token to authenticate and authorize the request, but you first need to parse it.
JWT is an open standard, and there are many great open-source libraries for parsing a JWT, verifying the cryptographic signature, and retrieving the data contained in the token. Pismo recommends that you use a well maintained and tested library. Here are some good choices:
If you need further assistance, you can contact your Technical Account Manager or open a ticket with the Service Desk.
A JWT token contains the claims listed in the table below. You can use them for the next steps in the verification process.
key | value | description |
---|---|---|
alg | RS256 | The algorithm used to generate the JWT signature. |
kid* | The ID of the key used to generate the signature. This must be one of the public keys provided by the Pismo platform. See Retrieving a list of public keys. Example: 1f88b81429cc451a335c2f5cdb3dfb34eb3bbc7f | |
iss | api.pismo.io | The issuer of the JWT. |
sub | The subject claim is the Account ID of the account associated with the request. Example: 1000001 | |
aud | Short for audience. The hostname of the intended recipient of the token. Example: https://www.example.com | |
iat | The time of the JWT generation in seconds since the UNIX epoch. Example: 1584990931 | |
exp | The time at which the token expires in seconds since the UNIX epoch. It can be a maximum of 3600 seconds later than the iat . Example: 1584990931 | |
body_hash | A SHA256 digest of the base64 encoded request message body. Example: dX5GKSLmA9o7lijdQrm7kR4om6rLTHssk1GJQiDGuUk= |
*Not all JWTs contain a kid
.
Retrieving a list of public keys
Pismo uses private and public keys to sign JWTs. You don't need to worry about the private keys – those are only used by Pismo – but you will need to get a list of the public keys. You do this by calling the public keys endpoint.
# These are endpoint where you can get Pismo's public keys ouside of India:
Sandbox: curl -i https://sandbox.pismolabs.io/robot/v1/metadata/x509/[email protected]
Production: curl -i https://prod.pismo.io/robot/v1/metadata/x509/[email protected]
# These are endpoint where you can get Pismo's public keys inside of India:
Sandbox: curl -i https://sandbox.pismolabs.io/robot/v1/metadata/x509/[email protected]
Production: curl -i https://prod.ind.pismo.io/robot/v1/metadata/x509/[email protected]
Once you get the list of public keys, you can cache it and use it to verify the signatures of subsequent requests.
"Should I call the public keys endpoint for every request?"
No. The public keys endpoint has a rate limit of 5 requests per second. To avoid getting rate limited, the public keys endpoint should only be used in the two scenarios listed below.
You should only call the public keys endpoint in the following scenarios:
-
You don’t have the list of public keys yet. You should call the public keys endpoint when you first log in.
-
To renew an expired list, according to the period specified in the Renewing your list of public keys section.
Renewing your list of public keys
The public keys in your list will eventually expire, so your service must include logic for refreshing the public keys. The Pismo platform relies on the HTTP cache-control mechanism to let you know when it's time to renew the public keys in your cache, so you can look at the Cache-Control
header for instructions on when to call the public keys endpoint again.
The max-age
component tells you the number of seconds that the requested content will remain valid.
Here's a sample of the contents of a cache-control header:
cache-control: public, max-age=22040, must-revalidate, no-transform
In this case, you can wait 22040 seconds (6 hours and 12 minutes) before renewing the cached public keys.
If you can't find the specified public key in your list of public keys, your list has expired and you need to renew it.
Verifying the token signature
After parsing the JWT, you need to verify that it was signed by Pismo. Start by finding out which public key the Pismo platform used to sign the request by looking for the kid
(key id) claim in the token.
Next, check to see if you already have the specified key in your local cache. If so, you're ready to verify the signature.
If you don't already have the key, request a new list of public keys using the public keys endpoint. See Retrieving a list of public keys.
Once you have the specified public key, you can use it to verify the signature. Pismo recommends that you use one of the libraries listed in the Parsing the JWT section to do this. For an example signature verification process, go to JSON Web Signature (JWS) with RSA.
If the signature matches, this verifies that the request came from Pismo, and you can proceed to the next step.
️ Signature not matching
If the key names match, but the signature doesn't, it may be the result of some sort of attack. Deny the request immediately and notify the security team.
A JWT might not contain a
kid
.In this case, you must parse over all available public keys and try verifying the signature with each one until you find one that matches.
Checking message integrity
The next step is to make sure the request payload was not modified on the way to your server. The JWT includes a hash of its payload. This lets you check message integrity.
- Get the
body_hash
value from the JWT. This is anSHA256
hash of the HTTP message body (usually in JSON). - Apply a
SHA256
hash function to the value and then encode the resulting hash inbase64
format. - Compare the computed hash with
body_hash
. If equal, the verification process is complete and successful.
Updated 9 months ago