Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,123 @@ composer update "craftcms/cms:^5" "craftcms/flysystem:^2.0" --with-all-dependenc

## Developer Features

### Signed HTTP Requests

Use the module’s request signer to sign a PSR-7 request with Cloud’s signing key before sending it to any destination that can verify HTTP message signatures.

```php
use craft\cloud\Module;
use GuzzleHttp\Psr7\Request;

$signer = Module::getInstance()->getRequestSigner();

$signedRequest = $signer->sign(new Request('POST', 'https://example.test/webhook'));
```

For Guzzle clients, use the signer’s handler stack helper so each request is signed when it is sent.

```php
use Craft;
use craft\cloud\Module;
use GuzzleHttp\RequestOptions;

$signer = Module::getInstance()->getRequestSigner();

$client = Craft::createGuzzleClient([
'handler' => $signer->createHandlerStack(),
]);

$client->post('https://example.test/webhook', [
RequestOptions::JSON => [
'event' => 'asset.saved',
],
]);
```

External systems can create compatible signatures without this PHP package. For example, install
[`http-message-sig`](https://www.npmjs.com/package/http-message-sig) in a Node-based build environment:

```bash
npm install http-message-sig
```

Then a Vercel build script can sign a Craft GraphQL request:

```js
import crypto from 'node:crypto';
import { signatureHeadersSync } from 'http-message-sig';

const method = 'POST';
const url = process.env.CRAFT_GRAPHQL_URL;
const signingKey = process.env.CRAFT_CLOUD_SIGNING_KEY;

if (!url || !signingKey) {
throw new Error('CRAFT_GRAPHQL_URL and CRAFT_CLOUD_SIGNING_KEY are required.');
}

const body = JSON.stringify({
query: `
query BuildData {
entries(section: "news") {
title
url
}
}
`,
});

const headers = {
'Content-Type': 'application/json',
...(process.env.CRAFT_GRAPHQL_TOKEN
? { Authorization: `Bearer ${process.env.CRAFT_GRAPHQL_TOKEN}` }
: {}),
};

const signer = {
keyid: 'hmac',
alg: 'hmac-sha256',
signSync(data) {
return crypto
.createHmac('sha256', signingKey)
.update(data)
.digest();
},
};

const created = new Date();
const signatureHeaders = signatureHeadersSync(
{ method, url, headers, body },
{
key: 'sig',
signer,
components: ['@method', '@target-uri'],
created,
expires: new Date(created.getTime() + 300_000),
},
);

const response = await fetch(url, {
method,
headers: {
...headers,
...signatureHeaders,
},
body,
});

if (!response.ok) {
throw new Error(`Craft GraphQL request failed: ${response.status}`);
}

const responseBody = await response.json();

if (responseBody.errors) {
throw new Error(`Craft GraphQL returned errors: ${JSON.stringify(responseBody.errors)}`);
}
```

The `@target-uri` value must be the exact URL being requested, including any query string.

### Template Helpers

#### `cloud.artifactUrl()`
Expand Down Expand Up @@ -109,7 +226,7 @@ Most configuration (to Craft and the extension itself) is handled directly by Cl
| `accessSecret` | `string` | AWS access secret, used in conjunction with the `accessKey`. |
| `accessToken` | `string` | AWS access token. |
| `redisUrl` | `string` | Connection string for the environment’s Redis instance. |
| `signingKey` | `string` | A secret value used to protect transform URLs against abuse. |
| `signingKey` | `string` | A secret value used to protect transform URLs and sign HTTP requests. |
| `useAssetBundleCdn` | `boolean` | Whether or not to enable the CDN for asset bundles. |
| `previewDomain` | `string\|null` | Set when accessing an environment from its [preview domain](https://craftcms.com/knowledge-base/cloud-domains#preview-domains). |
| `useQueue` | `boolean` | Whether or not to use Cloud’s SQS-backed queue driver. |
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
"bref/extra-php-extensions": "^3",
"craftcms/cms": "^4.6 || ^5",
"craftcms/flysystem": "^1.0.0 || ^2.0.0",
"craftcms/http-message-signatures": "^0.1",
"guzzlehttp/guzzle": "^7.4.5",
"league/flysystem-aws-s3-v3": "^3.15",
"league/uri": "^7.6",
"league/uri-components": "^7.6",
"yiisoft/yii2-redis": "^2.0",
"yiisoft/yii2-queue": "^2.3.7",
"phlak/semver": "^4.1",
"99designs/http-signatures": "^4.0",
"symfony/process": "^6",
"aws/aws-sdk-php": "^3.342.6",
"craftcms/yii2-cache-cascade": "^1.2.1"
Expand Down
Loading
Loading