Reputation: 5535
azure table query rest api is failing with AuthenticationFailed error.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>AuthenticationFailed</code>
<message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.</message>
</error>
The winjs app code snippet to form and make the rest call is:
var date = new Date().toGMTString().replace('UTC', 'GMT');
var xhrOption = {
type: 'GET',
url: url,
headers: {
'content-type': 'application/atom+xml;charset="utf-8"',
'content-length': 0,
dataserviceversion: '1.0;NetFx',
maxdataserviceversion: '2.0;NetFx',
'x-ms-version': '2011-08-18',
'x-ms-date': date,
accept: 'application/atom+xml,application/xml',
'Accept-Charset': 'UTF-8',
},
};
xhrOption.headers.Authorization = AuthorizationHeader().computeForTableService(options, xhrOption);
The code to compute the authorization header is little long. It is listed below:
_getSignatureStringForTableService: function getSignatureStringForTableService()
{
var headers = this.xhrOptions.headers;
var httpVerb = this.xhrOptions.type.toUpperCase();
var sigItems = [];
sigItems.push(httpVerb);
var contentMD5 = this._getHeaderOrDefault(headers, 'Content-MD5');
sigItems.push(contentMD5);
var contentType = this._getHeaderOrDefault(headers, 'content-type');
sigItems.push(contentType);
var date = this._getHeaderOrDefault(headers, 'x-ms-date');
if (!date)
date = this._getHeaderOrDefault(headers, 'Date');
sigItems.push(date);
var canonicalizedResource = this._getCanonicalizedResource();
sigItems.push(canonicalizedResource);
var result = sigItems.join('\n');
return result;
},
_getCanonicalizedResource: function getCanonicalizedResource()
{
var items = [];
var path;
if (config.storageAccount.isDevStorage)
path = "/" + config.storageAccount.name + '/' + config.storageAccount.name;
else
path = "/" + config.storageAccount.name;
path += "/" + this.options.resourcePath;
items.push(path);
var result = items.join('\n');
return result;
},
computeForTableService: function computeForTableService(options, xhrOptions)
{
this.options = options;
this.xhrOptions = xhrOptions;
var sig = this._computeSignatureForTableService();
var result = 'SharedKey ' + config.storageAccount.name + ':' + sig;
return result;
},
_computeSignatureForTableService: function computeSignatureForTableService()
{
var sigString = this._getSignatureStringForTableService();
// TODO: use crypto from windows api. currently uses, google cryptoJS lib
var key = CryptoJS.enc.Base64.parse(config.storageAccount.primaryKey);
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac.update(sigString);
var hash = hmac.finalize();
var result = hash.toString(CryptoJS.enc.Base64);
return result;
},
Interestingly, I have the whole code working fine 2 days before. I have updated service code to use updated azure nodejs sdk. I wonder if the update caused some incompat in the publisher/consumer code?
Other observations
Example: stringToSign
GET\n\napplication/atom+xml;charset="utf-8"\nWed, 5 Jun 2013 14:43:30 GMT\n/devstoreaccount1/devstoreaccount1/mytable()
Thanks for going through details.
Upvotes: 0
Views: 548
Reputation: 5535
Finally - the root cause of the bug is out. The issue is value of x-ms-date
header.
Expected value - Thu, 06 Jun 2013 08:09:50 GMT
Value computed in the code above - Thu, 6 Jun 2013 08:20:34 GMT
The 0
missing before the date is the root cause of this bug. Because of that, stringToSign used in computing the authorization header is incorrect. Hence, Authorization Header is incorrect leading to AuthenticationFailed error. This also explains the reason why this code worked couple of days back (end of may - date had two digits).
If someone from MS is reading this, it will be so much useful to have right amount of details along with the error code. AuthenticationFailed
error code alone does not give any clue to developer.
I had used azure storage blob rest api earlier. It returns better error for the same AuthenticationFailed error code. It sends across the expected stringToSign
and found stringToSign
along with the AuthenticationFailed error code. It is so much more helpful and bug gets resolved in couple of minutes.
Used Network monitor from Microsoft. Wrote c# code snippet to make the azure table query using azure .net sdk, and compared every header character by character to hit the issue.
Upvotes: 2