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:
- Create an Azure account with an active subscription. For details, see Create an account for free.
- Install Visual Studio.
- Create an Azure Communication Services resource. For details, see Create an Azure Communication Services resource. You'll need to record your resourceEndpoint and resourceAccessKey for this tutorial.
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
orPUT
) - 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 Program.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.UTF8.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.
- Prepare values for the headers to be signed.
- Specify the current timestamp using the Coordinated Universal Time (UTC) timezone.
- Get the request authority (DNS host name or IP address and the port number).
- Compute a content hash.
- Prepare a string to sign.
- Compute the signature.
- 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:
- Create an Azure account with an active subscription. For details, see Create an account for free.
- Download and install Python.
- Download and install Visual Studio Code or other IDE that supports Python.
- Create an Azure Communication Services resource. For details, see Create an Azure Communication Services resource. You'll need your resource_endpoint_name and resource_endpoint_secret for this tutorial.
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
orPUT
) - 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('utf-8')
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.
- Prepare values for the headers to be signed.
- Specify the current timestamp using the Coordinated Universal Time (UTC) timezone.
- Get the request authority (DNS host name or IP address and the port number).
- Compute a content hash.
- Prepare a string to sign.
- Compute the signature.
- 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: