AWS request signing (SigV4)

Note

Not part of core. Install it separately:

composer require kinetis/aws-sigv4

Wraps any PSR-18 HTTP client and signs every outgoing request with AWS Signature Version 4 before delegating to it — for talking to an AWS-signed endpoint (Amazon OpenSearch Service, API Gateway, and others) directly over HTTP rather than through a dedicated SDK client.

use Kinetis\AwsSigV4\SigV4SigningClient;
use Kinetis\RevoltHttpClient\AmpHttpClientFactory;
use Symfony\Component\HttpClient\Psr18Client;

$signedClient = new SigV4SigningClient(
    client: new Psr18Client(AmpHttpClientFactory::create()),
    region: 'us-east-1',
    service: 'es', // Amazon OpenSearch Service's signing name
);

$response = $signedClient->sendRequest($request);

$service is the AWS signing service name — "es" for Amazon OpenSearch Service, "execute-api" for API Gateway. There’s no default; guessing wrong produces a signature that fails verification rather than an obvious error.

Credentials

Resolved automatically the standard AWS way: AWS_ACCESS_KEY_ID/ AWS_SECRET_ACCESS_KEY, a shared credentials file, or an IAM role, whichever is available first. Pass a CredentialProvider directly as the fourth constructor argument to use something else instead:

use AsyncAws\Core\Credentials\Credentials;

$client = new SigV4SigningClient(
    client: new Psr18Client(AmpHttpClientFactory::create()),
    region: 'us-east-1',
    service: 'es',
    credentialProvider: new Credentials('AKIA...', 'secret-key'),
);

Amazon OpenSearch Service

OpenSearch\TransportFactory::setHttpClient() (see Search (OpenSearch)) accepts any PSR-18 client — SigV4SigningClient is one, so it drops in directly in place of the plain Psr18Client wrapper, replacing Basic auth with IAM-based signing:

use Kinetis\AwsSigV4\SigV4SigningClient;
use Kinetis\RevoltHttpClient\AmpHttpClientFactory;
use OpenSearch\Client;
use OpenSearch\EndpointFactory;
use OpenSearch\TransportFactory;
use Symfony\Component\HttpClient\Psr18Client;

// OpenSearch requires an explicit JSON Content-Type on every request.
$httpClient = new Psr18Client(AmpHttpClientFactory::create([
    'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'],
]));
$signedClient = new SigV4SigningClient(
    client: $httpClient,
    region: 'us-east-1',
    service: 'es',
    baseUri: 'https://search-my-domain.us-east-1.es.amazonaws.com',
);

$transport = (new TransportFactory())->setHttpClient($signedClient)->create();
$client = new Client($transport, new EndpointFactory());

Set baseUri on SigV4SigningClient itself, not on the wrapped Psr18Client — the OpenSearch client builds requests carrying only a path, and baseUri is what supplies the scheme and host for those. Leave it unset when the request you’re signing already carries a full URI of its own (e.g. a plain RequestInterface you built directly for API Gateway).

OpenSearchClientFactory::fromConfig() itself only ever builds the plain Basic-auth path — construct the client directly, as above, to use IAM/ SigV4 authentication instead.

See also