Sign an HTTP request

In this tutorial, you'll learn how to sign an HTTP request with an HMAC signature.

Note

We strongly encourage to use Azure SDKs. Approach described here is a fallback option for cases when Azure SDKs can't be used for any reason.

Prerequisites

Before you get started, make sure to:

Sign an HTTP request with C#

Access key authentication uses a shared secret key to generate an HMAC signature for each HTTP request. This signature is generated with the SHA256 algorithm and is sent in the Authorization header by using the HMAC-SHA256 scheme. For example:

Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"

The hmac-sha256-signature consists of:

  • HTTP verb (for example, GET or PUT)
  • HTTP request path
  • x-ms-date
  • Host
  • x-ms-content-sha256

Setup

The following steps describe how to construct the authorization header.

Create a new C# application

In a console window, such as cmd, PowerShell, or Bash, use the dotnet new command to create a new console app with the name SignHmacTutorial. This command creates a simple "Hello World" C# project with a single source file: Program.cs.

dotnet new console -o SignHmacTutorial

Change your directory to the newly created app folder. Use the dotnet build command to compile your application.

cd SignHmacTutorial
dotnet build

Install the package

Install the package Newtonsoft.Json that's used for body serialization.

dotnet add package Newtonsoft.Json

Update the Main method declaration to support async code. Use the following code to begin.

using System;
using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SignHmacTutorial
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Azure Communication Services - Sign an HTTP request Tutorial");
            // Tutorial code goes here.
        }
    }
}

Create a request message

For this example, we'll sign a request to create a new identity by using the Communication Services Authentication API (version 2021-03-07).

Add the following code to the Main method.

string resourceEndpoint = "resourceEndpoint";
// Create a uri you are going to call.
var requestUri = new Uri($"{resourceEndpoint}/identities?api-version=2021-03-07");
// Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body
var body = new
    {
        createTokenWithScopes = new[] { "chat" }
    };

var serializedBody = JsonConvert.SerializeObject(body);

var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri)
{
    Content = new StringContent(serializedBody, Encoding.UTF8, "application/json")
};

Replace resourceEndpoint with your real resource endpoint value.

Create a content hash

The content hash is a part of your HMAC signature. Use the following code to compute the content hash. You can add this method to Progam.cs under the Main method.

static string ComputeContentHash(string content)
{
    using (var sha256 = SHA256.Create())
    {
        byte[] hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(content));
        return Convert.ToBase64String(hashedBytes);
    }
}

Compute a signature

Use the following code to create a method for computing your HMAC signature.

 static string ComputeSignature(string stringToSign)
{
    string secret = "resourceAccessKey";
    using (var hmacsha256 = new HMACSHA256(Convert.FromBase64String(secret)))
    {
        var bytes = Encoding.ASCII.GetBytes(stringToSign);
        var hashedBytes = hmacsha256.ComputeHash(bytes);
        return Convert.ToBase64String(hashedBytes);
    }
}

Replace resourceAccessKey with an access key of your real Communication Services resource.

Create an authorization header string

We'll now construct the string that we'll add to our authorization header.

  1. Prepare values for the headers to be signed.
    1. Specify the current timestamp using the Coordinated Universal Time (UTC) timezone.
    2. Get the request authority (DNS host name or IP address and the port number).
    3. Compute a content hash.
  2. Prepare a string to sign.
  3. Compute the signature.
  4. Concatenate the string, which will be used in the authorization header.

Add the following code to the Main method.

// Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
// Get the host name corresponding with the 'host' header.
var host = requestUri.Authority;
// Compute a content hash for the 'x-ms-content-sha256' header.
var contentHash = ComputeContentHash(serializedBody);

// Prepare a string to sign.
var stringToSign = $"POST\n{requestUri.PathAndQuery}\n{date};{host};{contentHash}";
// Compute the signature.
var signature = ComputeSignature(stringToSign);
// Concatenate the string, which will be used in the authorization header.
var authorizationHeader = $"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}";

Add headers to requestMessage

Use the following code to add the required headers to your requestMessage.

// Add a date header.
requestMessage.Headers.Add("x-ms-date", date);

// Add a host header.
// In C#, the 'host' header is added automatically by the 'HttpClient'. However, this step may be required on other platforms such as Node.js.

// Add a content hash header.
requestMessage.Headers.Add("x-ms-content-sha256", contentHash);

// Add an authorization header.
requestMessage.Headers.Add("Authorization", authorizationHeader);

Test the client

Call the endpoint by using HttpClient, and check the response.

HttpClient httpClient = new HttpClient
{
    BaseAddress = requestUri
};
var response = await httpClient.SendAsync(requestMessage);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);

Prerequisites

Before you get started, make sure to:

Sign an HTTP request with Python

Access key authentication uses a shared secret key to generate an HMAC signature for each HTTP request. This signature is generated with the SHA256 algorithm and is sent in the Authorization header by using the HMAC-SHA256 scheme. For example:

Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"

The hmac-sha256-signature consists of:

  • HTTP verb (for example, GET or PUT)
  • HTTP request path
  • x-ms-date
  • Host
  • x-ms-content-sha256

Setup

The following steps describe how to construct the authorization header.

Create a new Python script

Open Visual Studio Code or other IDE or editor of your choice and create a new file named sign_hmac_tutorial.py. Save this file to a known folder.

Add necessary imports

Update the sign_hmac_tutorial.py script with the following code to begin.

import base64
import hashlib
import hmac
import json
from datetime import datetime, timezone
from urllib import request

Prepare data for the request

For this example, we'll sign a request to create a new identity by using the Communication Services Authentication API (version 2021-03-07).

Add the following code to the sign_hmac_tutorial.py script.

  • Replace resource_endpoint_name with your real resource endpoint name value. This value can be found in Overview section of your Azure Communication Services resource. It's the value of "Endpoint" after "https://".
  • Replace resource_endpoint_secret with your real resource endpoint secret value. This value can be found in Keys section of your Azure Communication Services resource. It's the value of "Key" - either primary or secondary.
host = "resource_endpoint_name"
resource_endpoint = f"https://{host}"
path_and_query = "/identities?api-version=2021-03-07"
secret = "resource_endpoint_secret"

# Create a uri you are going to call.
request_uri = f"{resource_endpoint}{path_and_query}"

# Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body.
body = { "createTokenWithScopes": ["chat"] }

serialized_body = json.dumps(body)
content = serialized_body.encode("utf-8")

Create a content hash

The content hash is a part of your HMAC signature. Use the following code to compute the content hash. You can add this method to sign_hmac_tutorial.py script.

def compute_content_hash(content):
    sha_256 = hashlib.sha256()
    sha_256.update(content)
    hashed_bytes = sha_256.digest()
    base64_encoded_bytes = base64.b64encode(hashed_bytes)
    content_hash = base64_encoded_bytes.decode('utf-8')
    return content_hash

Compute a signature

Use the following code to create a method for computing your HMAC signature.

def compute_signature(string_to_sign, secret):
    decoded_secret = base64.b64decode(secret)
    encoded_string_to_sign = string_to_sign.encode('ascii')
    hashed_bytes = hmac.digest(decoded_secret, encoded_string_to_sign, digest=hashlib.sha256)
    encoded_signature = base64.b64encode(hashed_bytes)
    signature = encoded_signature.decode('utf-8')
    return signature

Get current UTC timestamp according to the RFC1123 standard

Use the following code to get desired date format independent of locale settings.

def format_date(dt):
    days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    utc = dt.utctimetuple()

    return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format(
    days[utc.tm_wday],
    utc.tm_mday,
    months[utc.tm_mon-1],
    utc.tm_year,
    utc.tm_hour, 
    utc.tm_min, 
    utc.tm_sec)

Create an authorization header string

We'll now construct the string that we'll add to our authorization header.

  1. Prepare values for the headers to be signed.
    1. Specify the current timestamp using the Coordinated Universal Time (UTC) timezone.
    2. Get the request authority (DNS host name or IP address and the port number).
    3. Compute a content hash.
  2. Prepare a string to sign.
  3. Compute the signature.
  4. Concatenate the string, which will be used in the authorization header.

Add the following code to the sign_hmac_tutorial.py script.

# Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
utc_now = datetime.now(timezone.utc)
date = format_date(utc_now)
# Compute a content hash for the 'x-ms-content-sha256' header.
content_hash = compute_content_hash(content)

# Prepare a string to sign.
string_to_sign = f"POST\n{path_and_query}\n{date};{host};{content_hash}"
# Compute the signature.
signature = compute_signature(string_to_sign, secret)
# Concatenate the string, which will be used in the authorization header.
authorization_header = f"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}"

Add headers

Use the following code to add the required headers.

request_headers = {}

# Add a date header.
request_headers["x-ms-date"] = date

# Add content hash header.
request_headers["x-ms-content-sha256"] = content_hash

# Add authorization header.
request_headers["Authorization"] = authorization_header

# Add content type header.
request_headers["Content-Type"] = "application/json"

Test the client

Call the endpoint and check the response.

req = request.Request(request_uri, content, request_headers, method='POST')
with request.urlopen(req) as response:
  response_string = json.load(response)
print(response_string)

Clean up resources

To clean up and remove a Communication Services subscription, delete the resource or resource group. Deleting the resource group also deletes any other resources associated with it. You can find out more about cleaning up Azure Communication Services resources and cleaning Azure Functions resources.

Next steps

You might also want to: