Authentication & Security
The SDK authenticates each request with an HMAC signature, client identity, and authorized domain.
How request signing works
- The SDK serializes array payloads with
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES. - It signs the exact raw request body with
hash_hmac('sha256', $body, $clientSecret). - The hex signature is sent as
X-Signature. - The server resolves
X-Client-Id, validates the signature, and checksX-Client-Domainagainst the client's allowlist.
use ZatcaX\SDK\Http\RequestSigner;
$rawJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$signature = RequestSigner::sign($rawJson, $clientSecret);
Do not re-encode the payload after signing. Any byte-level change produces a different HMAC.
Headers generated by ZatcaClient
| Header | Value | When sent |
|---|---|---|
Content-Type | application/json | Always |
Accept | application/json | Always |
X-Signature | HMAC-SHA256 hex digest | Always, including GET requests signed with an empty body |
X-Client-Id | Configured client ID | When a client ID is configured |
X-Client-Domain | Configured or detected host | When a domain is available |
Origin | https://{domain} | Alongside the client domain |
Domain allowlisting
The owner dashboard can restrict a client to one or more hosts. Exact hosts, wildcard subdomains such as *.example.com, and local aliases are supported by the server. A rejected domain produces HTTP 403 and ZatcaDomainUnauthorizedException.
Operational security checklist
- Store credentials in environment variables or a secrets manager.
- Never send the API secret to browser code, mobile clients, logs, or exception pages.
- Use HTTPS and keep SSL peer verification enabled in production.
- Restrict allowed domains and rotate the API secret after suspected exposure.
- Use a separate portal password; it is not the HMAC client secret.
- Do not log certificate private keys, OTP values, or authorization tokens.