Skip to main content

Signatures

To remove the need for authentication on the receiving side, the Star Community API uses signatures on all webhook calls to authenticate itself and provide for a checksum/tamper protection.

The signature is provided in the X-Signature header on every request and should be validated before processing the event body.

Verification steps

  1. Fetch the public key from the appropriate endpoint: https://api.test.starcommunity.app/.well-known/webhooks.key (test) or https://api.starcommunity.app/.well-known/webhooks.key (production).
  2. Cache the key for at least a few minutes, possibly for hours. Do not cache forever.
  3. Get the request body and X-Signature headers
  4. Decode the Base64 encoded signature header
  5. Verify the request body against the decoded signature using the public key with the RSA-SHA256 algorithm
  6. Only process the webhook request if the signature is correct

Testing your implementation

You can fetch an example JSON body with a X-Signature header from https://api.test.starcommunity.app/.well-known/webhook-example-body (test) or https://api.starcommunity.app/.well-known/webhook-example-body (production) using a HTTP GET request. Normally, this body and header would be sent in HTTP POST to your server directly.

Example Code

PHP

<?php

// Fetch public key from URL
$publicKeyUrl = "https://api.test.starcommunity.app/.well-known/webhooks.key"; // Replace with the URL you would like to use
$publicKey = file_get_contents($publicKeyUrl);
if ($publicKey === false) {
die("Failed to fetch public key from URL");
}

// TODO: Implement caching of the key, at least for a few minutes

// Example JSON body and X-Signature header
$jsonBody = '{"key": "value"}'; // Replace with actual JSON body recieved in your framework
$xSignature = "BASE64_SIGNATURE_HERE"; // Replace with actual signature received from the X-Signature header

// Decode the signature
$decodedSignature = base64_decode($xSignature);
if ($decodedSignature === false) {
die("Invalid Base64 signature");
}

// Verify the signature
$isValid = openssl_verify($jsonBody, $decodedSignature, $publicKey, OPENSSL_ALGO_SHA256);
if ($isValid === 1) {
echo "Signature is valid\n";
} elseif ($isValid === 0) {
echo "Signature is invalid\n";
} else {
echo "Error during signature verification\n";
}

?>

NodeJS

import { createVerify } from "crypto";

// Example JSON body and X-Signature header
const jsonBody = '{"key": "value"}'; // Replace with the actual JSON body from your frameework
const xSignature = "BASE64_SIGNATURE_HERE"; // Replace with the actual signature from the X-Signature header

// URL to fetch the public key
const publicKeyUrl = "https://api.test.starcommunity.app/.well-known/webhooks.key"; // Replace with the actual URL you would like to use

// Fetch the public key
const response = await fetch(publicKeyUrl);
if (!response.ok) {
throw new Error(`Failed to fetch public key: ${response.statusText}`);
}
const publicKey = await response.text();

// TODO: Implement caching of the key, at least for a few minutes

// Create a verifier
const verifier = createVerify("RSA-SHA256");
verifier.update(jsonBody);
verifier.end();

// Decode the Base64 signature
const decodedSignature = Buffer.from(xSignature, "base64");

// Verify the signature
const isValid = verifier.verify(publicKey, decodedSignature);

if (isValid) {
console.log("Signature is valid");
} else {
console.log("Signature is invalid");
}

Python

import base64
import requests
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256

# Fetch public key from URL
public_key_url = "https://api.test.starcommunity.app/.well-known/webhooks.key" # Replace with the URL you would like to use
response = requests.get(public_key_url)
response.raise_for_status()
public_key_pem = response.text

# TODO: Implement caching of the key, at least for a few minutes

# Load the public key
public_key = RSA.import_key(public_key_pem)

# Example JSON body and X-Signature header
json_body = '{"key": "value"}' # Replace with actual JSON body recieved in your framework
x_signature = "BASE64_SIGNATURE_HERE" # Replace with actual signature received from the X-Signature header

# Compute the SHA256 hash of the JSON body
hashed_body = SHA256.new(json_body.encode('utf-8'))

# Decode the Base64 signature
decoded_signature = base64.b64decode(x_signature)

# Verify the signature
try:
pkcs1_15.new(public_key).verify(hashed_body, decoded_signature)
print("Signature is valid")
except (ValueError, TypeError):
print("Signature verification failed")