Example code

  • Using a delegating handler you can separate the generation of the authorization header from the actual request.

    using (var client = HttpClientFactory.Create(new HMacDelegatingHandler("your-appkey-id","your-appkey-secret")))
    {
        var result = await client.GetAsync("https://api.combell.com/v1/domains");
        // Do something with the result here.
    }

    The delegating handler:

    This implements the signature generation and sets the Authorization header.

    public class HMacDelegatingHandler : DelegatingHandler
    {
        private string _appId;
        private string _appSecret;
    
        internal HMacDelegatingHandler(string appId, string appSecret)
        {
            _appId = appId;
            _appSecret = appSecret;
        }
    
        protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = null;
            var requestContentBase64String = string.Empty;
    
            // Calculate UNIX time
            var epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
            var timeSpan = DateTime.UtcNow - epochStart;
            var requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
    
            // Create random nonce for each request
            var nonce = Guid.NewGuid().ToString("N");
    
            // Check if the request contains body, usually will be null when request method is GET or DELETE
            if (request.Content != null)
            {
                var content = await request.Content.ReadAsByteArrayAsync();
                using (var md5 = MD5.Create())
                { 
                    // Hash the request body, any change in request body will result in different hash, we'll incure message integrity
                    var requestContentHash = md5.ComputeHash(content);
                    requestContentBase64String = Convert.ToBase64String(requestContentHash);    
                }
            }
    
            // Generate the hashed signature
            var signatureData = 
                String.Format("{0}{1}{2}{3}{4}{5}", 
                    _appId,
                    request.Method.Method.ToLower(),
                    WebUtility.UrlEncode(string.Format("{0}{1}", request.RequestUri.LocalPath, request.RequestUri.Query)), 
                    requestTimeStamp, 
                    nonce, 
                    requestContentBase64String);
    
            var encoding = Encoding.UTF8;
            var signatureBytes = encoding.GetBytes(signatureData);
            var secretKeyByteArray = encoding.GetBytes(_appSecret);
    
            using (var hmac = new HMACSHA256(secretKeyByteArray))
            {
                var signatureHash = hmac.ComputeHash(signatureBytes);
                var requestSignatureBase64String = Convert.ToBase64String(signatureHash);
             
                // Set the values in the Authorization header using custom scheme (hmac)
                request.Headers.Authorization = new AuthenticationHeaderValue(
                    "hmac", 
                    string.Format("{0}:{1}:{2}:{3}", 
                        _appId, 
                        requestSignatureBase64String, 
                        nonce, 
                        requestTimeStamp));
            }
    
            response = await base.SendAsync(request, cancellationToken);
    
            return response;
        }    
    }
  • Use our PHP package to find examples of all API calls and a Guzzle handler to simplify authorization: https://github.com/combell/combell-api.

    Reference guzzle in composer.json

    {
        "require": {
            "guzzlehttp/guzzle": "~6.0"
        }
    }

    Using a delegating handler you can separate the generation of the authorization header from the actual request.

    include('vendor/autoload.php');
    include('HmacHandler.php');
    $hmacHandler = new HmacHandler('your-apikey-id','your-apikey-secret');
    $stack = \GuzzleHttp\HandlerStack::create();
    $stack->push($hmacHandler);
    $client = new \GuzzleHttp\Client(['handler' => $stack, 'debug' => true]);
    $response = $client->get('https://api.combell.com/v1/domains');

    The delegating handler:

    This implements the signature generation and sets the Authorization header.

    class HmacHandler
    {
        protected $apiKey;
        protected $apiSecret;
    
        public function __construct($apiKey, $apiSecret)
        {
            $this->apiKey = $apiKey;
            $this->apiSecret = $apiSecret;
        }
    
        protected function sign(Psr\Http\Message\RequestInterface $request)
        {
            $time = time();
            $nonce = uniqid();
            $path = (string) $request->getUri()->getPath();
            $query = (string) $request->getUri()->getQuery();
            $body = (string) $request->getBody()->getContents();
    
            if ($query !== '') {
                $path .= '?' . $query;
            }
    
            if ($body !== '') {
                $body = base64_encode(md5($body, true));
            }
    
            $valueToSign = $this->apiKey
                . strtolower($request->getMethod())
                . urlencode($path)
                . $time
                . $nonce
                . $body;
    
            $signedValue = hash_hmac('sha256', $valueToSign, $this->apiSecret, true);
            $signature = base64_encode($signedValue);
    
            return sprintf('hmac %s:%s:%s:%s', $this->apiKey, $signature, $nonce, $time);
        }
    
        public function __invoke(callable $handler)
        {
            return function (Psr\Http\Message\RequestInterface $request, array $options) use ($handler) {
                $request = $request->withHeader('Authorization', $this->sign($request));
                return $handler($request, $options);
            };
        }
    }