How to make a valid hmac authentication header

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering HMAC Authentication: How to Generate a Valid Header for API Requests

As developers working with external APIs, securing communication is paramount. Hash-based Message Authentication Code (HMAC) authentication is a robust way to ensure that the request has not been tampered with and originates from a legitimate source. However, implementing it correctly—especially when dealing with specific API requirements like the Combell endpoint you mentioned—can be deceptively tricky.

If you are encountering a 401 Unauthorized error with an HMAC signature, it almost always means there is a mismatch between what you are signing and how the server expects that signature to be formatted. Let’s break down the common pitfalls and show you how to construct a valid HMAC header using Guzzle and PHP.

Understanding the Anatomy of HMAC Authentication

HMAC relies on a cryptographic hash function (like SHA256) applied to a secret key and the data you want to verify. The crucial step is defining exactly what data constitutes the "message" being signed. For most financial or sensitive APIs, this message is a concatenation of request details, timestamps, and your secret key.

The Combell API documentation outlines specific steps for generating this signature. Following these steps precisely is the only way to avoid the authorization_hmac_invalid error.

Common Pitfalls in HMAC Implementation

Your current approach attempts to build a complex string ($valueToSign) from several variables, which is good, but the exact concatenation order and the inclusion of non-standard elements (like combining path queries directly) are often where errors occur.

  1. Inconsistent Encoding: Ensure every piece of data being signed (timestamps, nonces, paths) is encoded consistently (usually as URL-safe base64 or raw bytes before hashing).
  2. Incorrect Payload: The payload you hash must exactly match the format specified by the API documentation. If the API requires a specific structure (e.g., timestamp.nonce.path), deviating causes failure.
  3. Key Management: Ensure your $api_secret is correctly loaded and used as the secret key for the HMAC calculation, not just appended to the final output.

A Corrected Approach for Guzzle and HMAC

Let's refine your hmacHandler function based on standard practices and the likely requirements of secure API interactions. We will focus on ensuring that the data being signed is strictly deterministic and matches external specifications.

In this example, we assume the API requires signing a combination of the method, path, timestamp, and nonce.

class GuzzleController extends Controller
{
    protected $api_key;
    protected $api_secret;

    public function __construct()
    {
        $this->api_key = env('API_KEY');
        $this->api_secret = env('API_SECRET');
    }

    protected function hmacHandler() {
        $key = $this->api_key;
        $req_method = 'get';
        $path = 'https://api.combell.com/v2/accounts'; // Use the full path for signing context
        $timestamp = time();
        // Generate a unique nonce (Number used once)
        $nonce = bin2hex(random_bytes(8)); 

        // Construct the exact string payload to be signed, strictly following API rules
        // Note: The exact format below is an educated guess based on common HMAC implementations.
        // You MUST verify this against the official Combell documentation.
        $valueToSign = $req_method . '.' . $path . '.' . $timestamp . '.' . $nonce;

        // Calculate the HMAC signature using SHA256
        $signedValue = hash_hmac('sha256', $valueToSign, $this->api_secret, true);

        // Encode the result for safe transmission (Base64 is standard)
        $signature = base64_encode($signedValue);

        // Return the final formatted header string as required by the API
        return sprintf('HMAC %s:%s:%s:%s', $this->api_key, $signature, $nonce, $timestamp);
    }

    public function getTestData() {
        $client = new Client();
        $uri = 'https://api.combell.com/v2/accounts';
        
        // Apply the generated HMAC string to the Authorization header
        $header = ['headers' => [
            'Authorization' => $this->hmacHandler()
        ]];

        try {
            $res = $client->get($uri, $header);
            return json_decode($res->getBody()->getContents(), true);
        } catch (\GuzzleHttp\Exception\RequestException $e) {
            // Handle specific HTTP errors gracefully
            echo "Request failed: " . $e->getMessage();
            return null;
        }
    }
}

Integrating with Guzzle Best Practices

When working with HTTP clients like Guzzle, the key is separating the logic for signature generation from the actual request execution. As you see above, we encapsulate the complex signing logic into a dedicated method (hmacHandler). This adheres to the principle of separation of concerns, making your code cleaner and easier to debug—a core tenet in building robust applications, much like how well-structured projects are built on frameworks like those promoted by Laravel (which emphasizes clean MVC architecture).

By ensuring that the string you pass to hash_hmac is exactly what the API expects for signing, and correctly encoding the final result before placing it in the Authorization header, you resolve the ambiguity that led to the invalid HMAC error. Always treat external API documentation as your primary source of truth when implementing authentication schemes.

Conclusion

Generating a valid HMAC authentication header is less about complex PHP functions and more about meticulous adherence to the external API's signing specification. By carefully constructing the payload you sign, ensuring correct encoding, and keeping your logic modular, you can reliably secure your requests. Debugging these issues often boils down to tracing the data flow from your application (Guzzle) through your custom handler back to the server's expectations. Trust the process, verify the documentation, and you will achieve stable, authenticated API communication.