A PHP Guzzle middleware for signing HTTP requests using HTTP Message Signatures (RFC 9421), specifically tailored for the web-bot-auth
profile as discussed by Cloudflare. This version uses Ed25519 signatures via the libsodium
PHP extension.
- PHP 7.4+ (libsodium is bundled with PHP 7.2+, but this package uses features from 7.4+)
- The
sodium
PHP extension must be enabled. - GuzzleHTTP 7.0+
Install the package via Composer:
composer require olipayne/guzzle-web-bot-auth-middleware
To use this middleware, you need an Ed25519 private key, its corresponding public key (in JWK format hosted publicly), and a keyid
(JWK Thumbprint of the public key). The middleware uses alg: "eddsa"
in the Signature-Input
header.
This package includes a utility script to generate everything you need for Ed25519:
-
Make the script executable (if you haven't already):
chmod +x vendor/olipayne/guzzle-web-bot-auth-middleware/bin/generate-keys.php
-
Run the script from your project's root directory (or any directory where you want the key files to be saved):
php vendor/olipayne/guzzle-web-bot-auth-middleware/bin/generate-keys.php
(Path might vary based on your setup. If installed as a library, it's in
vendor/olipayne/guzzle-web-bot-auth-middleware/bin/
.)The script will:
- Create
ed25519_private.key
(containing the base64 encoded Ed25519 private key - KEEP THIS SAFE AND SECRET!). - Create
ed25519_public.key
(containing the base64 encoded Ed25519 public key, for your reference). - Output the Base64 Encoded Ed25519 Private Key: You'll pass this (or the path to
ed25519_private.key
) to the middleware. - Output the JWK Thumbprint (kid): This is the
keyid
for the middleware. - Output the Full Ed25519 JWK: This is the JSON structure of your public key to host publicly.
Example output snippet:
Base64 encoded Ed25519 private key saved to: ed25519_private.key (Used by the middleware) Base64 encoded Ed25519 public key saved to: ed25519_public.key (For reference or other uses) --- Configuration for WebBotAuthMiddleware (Ed25519) --- Base64 Encoded Ed25519 Private Key (content of 'ed25519_private.key', for middleware constructor): YOUR_BASE64_ENCODED_ED25519_PRIVATE_KEY JWK Thumbprint (use as 'keyId'): YOUR_GENERATED_ED25519_KEY_ID Full Ed25519 JWK (host this at your 'signatureAgent' URL, typically in a JWKSet): { "kty": "OKP", "crv": "Ed25519", "x": "...base64url_encoded_public_key...", "kid": "YOUR_GENERATED_ED25519_KEY_ID", "alg": "EdDSA", "use": "sig" } ...
- Create
-
Host Your Public Key (JWKSet) The
Signature-Agent
header in your requests will point to a URL where the server can fetch your public key (the "Full Ed25519 JWK" from the script) to verify the signature.A common practice is
https://your-bot.example.com/.well-known/jwks.json
. The content ofjwks.json
should be:{ "keys": [ // The "Full Ed25519 JWK" output from the script goes here { "kty": "OKP", "crv": "Ed25519", "x": "...base64url_encoded_public_key...", "kid": "YOUR_GENERATED_ED25519_KEY_ID", "alg": "EdDSA", "use": "sig" } ] }
Ensure this URL is publicly accessible.
If you already have a base64 encoded Ed25519 public key and need its JWK and kid
:
- Make the
generate-jwk.php
script executable:chmod +x vendor/olipayne/guzzle-web-bot-auth-middleware/bin/generate-jwk.php
- Run it with your base64 encoded Ed25519 public key string or the path to a file containing it:
This will output the
# Using a string php vendor/olipayne/guzzle-web-bot-auth-middleware/bin/generate-jwk.php YOUR_BASE64_PUBLIC_KEY_STRING # Using a file php vendor/olipayne/guzzle-web-bot-auth-middleware/bin/generate-jwk.php path/to/your/ed25519_public.key
kid
and the full JWK for your existing public key. - You will need your corresponding Ed25519 private key (base64 encoded) to configure the middleware.
- Host the public JWK as described in Step 3 of the "Easiest Setup".
Provide the base64 encoded Ed25519 private key (or the path to the file like ed25519_private.key
), your keyid
, and your signatureAgent
URL to the middleware.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Olipayne\GuzzleWebBotAuth\WebBotAuthMiddleware;
// Ensure libsodium is available
if (!extension_loaded('sodium')) {
die('Libsodium extension is required!');
}
// 1. Create a Guzzle HandlerStack
$stack = HandlerStack::create();
// 2. Configure the WebBotAuthMiddleware
// Option A: Path to the file containing the base64 encoded private key
$privateKeyPath = 'path/to/your/ed25519_private.key';
// Option B: The base64 encoded private key string directly
// $base64PrivateKey = 'YOUR_BASE64_ENCODED_ED25519_PRIVATE_KEY_FROM_SCRIPT_OUTPUT';
$keyId = 'YOUR_GENERATED_ED25519_KEY_ID'; // The JWK Thumbprint from the script output
$signatureAgentUrl = 'https://your-bot.example.com/.well-known/jwks.json'; // URL to your public JWKSet
$botAuthMiddleware = new WebBotAuthMiddleware(
$privateKeyPath, // or $base64PrivateKey
$keyId,
$signatureAgentUrl
// Optional tag and expires duration remain the same
);
// 3. Push the middleware onto the stack
$stack->push($botAuthMiddleware);
// 4. Create the Guzzle client with the handler stack
$client = new Client(['handler' => $stack]);
// Requests are now signed using Ed25519
try {
$response = $client->get('https://target-service.example.com/api/data');
// ...
} catch (\Exception $e) {
// ...
}
?>
- Covered Components:
("@authority" "signature-agent")
- Signature Algorithm (in
Signature-Input
):alg="eddsa"
(implies Ed25519 with this library) - JWK Algorithm (
alg
in JWK):EdDSA
The middleware uses sodium_crypto_sign_detached
for Ed25519 signatures. The Signature-Input
header includes an alg="eddsa"
parameter. The JWK for the public key uses kty: "OKP"
(Octet Key Pair) and crv: "Ed25519"
.
Contributions are welcome! Please feel free to submit pull requests or open issues.
This package is open-sourced software licensed under the MIT license.