I'm trying to use Elixir to access Azure Storage Services via their REST API but I'm having difficulty getting the Authentication Header to work. I am able to connect if I use the ex_azure package (wrapper for erlazure) but not when I try to build the request and use HTTPoison.
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:00000000-0000-0000-0000-000000000000\nTime:2017-08-02T21:46:08.6488342Z</Message>
<AuthenticationErrorDetail>The MAC signature found in the HTTP request '<signature>' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\nWed, 02 Aug 2017 21:46:08
GMT\nx-ms-date-h:Wed, 02 Aug 2017 21:46:08 GMT\nx-ms-version-h:2017-05-10\n/storage_name/container_name?comp=list'.</AuthenticationErrorDetail>
</Error>
After 1st Edit
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:00000000-0000-0000-0000-000000000000\nTime:2017-08-03T03:03:57.1385277Z</Message>
<AuthenticationErrorDetail>The MAC signature found in the HTTP request '<signature>' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Thu, 03 Aug
2017 03:03:57 GMT\nx-ms-version:2017-04-17\n/storage_name/container_name\ncomp:list\nrestype:container'.</AuthenticationErrorDetail>
</Error>
# mix.exs
defp deps do
{:httpoison, "~> 0.12"}
{:timex, "~> 3.1"}
end
string_to_sign
) right?# account credentials
storage_name = "storage_name"
container_name = "container_name"
storage_key = "storage_key"
storage_service_version = "2017-04-17" # fixed version
request_date =
Timex.now
|> Timex.format!("{RFC1123}") # Wed, 02 Aug 2017 00:52:10 +0000
|> String.replace("+0000", "GMT") # Wed, 02 Aug 2017 00:52:10 GMT
# set canonicalized headers
x_ms_date = "x-ms-date:#{request_date}"
x_ms_version = "x-ms-version:#{storage_service_version}"
# assign values for string_to_sign
verb = "GET\n"
content_encoding = "\n"
content_language = "\n"
content_length = "\n"
content_md5 = "\n"
content_type = "\n"
date = "\n"
if_modified_since = "\n"
if_match = "\n"
if_none_match = "\n"
if_unmodified_since = "\n"
range = "\n"
canonicalized_headers = "#{x_ms_date}\n#{x_ms_version}\n"
canonicalized_resource = "/#{storage_name}/#{container_name}\ncomp:list\nrestype:container" # removed timeout. removed space
# concat string_to_sign
string_to_sign =
verb <>
content_encoding <>
content_language <>
content_length <>
content_md5 <>
content_type <>
date <>
if_modified_since <>
if_match <>
if_none_match <>
if_unmodified_since <>
range <>
canonicalized_headers <>
canonicalized_resource
# decode storage_key
{:ok, decoded_key} =
storage_key
|> Base.decode64
# sign and encode string_to_sign
signature =
:crypto.hmac(:sha256, decoded_key, string_to_sign)
|> Base.encode64
# build authorization header
authorization_header = "SharedKey #{storage_name}:#{signature}"
# build request and use HTTPoison
url = "https://storage_name.blob.core.windows.net/container_name?restype=container&comp=list"
headers = [ # "Date": request_date,
"x-ms-date": request_date, # fixed typo
"x-ms-version": storage_service_version, # fixed typo
# "Accept": "application/json",
"Authorization": authorization_header]
options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 500]
HTTPoison.get(url, headers, options)
Some sources I used/tried...
A few issues I noticed:
Date
request header in your request but it is not included in your string_to_sign
. Either include this header in your string_to_sign
or remove this header from request headers.timeout:30
in your canonicalized_resource
but it is not included in your request URL. Again, either add timeout=30
in your request querystring or remove timeout:30
from canonicalized_resource
.x-ms-date-h
and x-ms-version-h
. Shouldn't they be x-ms-date
and x-ms-version
respectively?